summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format9
-rw-r--r--.editorconfig2
-rw-r--r--.travis.yml6
-rw-r--r--AUTHORS.md29
-rw-r--r--CODEOWNERS2
-rw-r--r--DONORS.md32
-rw-r--r--SConstruct16
-rw-r--r--core/SCsub8
-rw-r--r--core/class_db.cpp6
-rw-r--r--core/class_db.h14
-rw-r--r--core/command_queue_mt.h24
-rw-r--r--core/dictionary.cpp5
-rw-r--r--core/dictionary.h1
-rw-r--r--core/dvector.h2
-rw-r--r--core/global_constants.cpp4
-rw-r--r--core/hashfuncs.h7
-rw-r--r--core/image.cpp174
-rw-r--r--core/image.h12
-rw-r--r--core/io/http_client.cpp69
-rw-r--r--core/io/http_client.h1
-rw-r--r--core/io/multiplayer_api.cpp7
-rw-r--r--core/io/multiplayer_api.h2
-rw-r--r--core/io/stream_peer_ssl.cpp17
-rw-r--r--core/io/stream_peer_ssl.h8
-rw-r--r--core/math/a_star.cpp24
-rw-r--r--core/math/a_star.h2
-rw-r--r--core/math/math_funcs.h18
-rw-r--r--core/math/matrix3.cpp24
-rw-r--r--core/math/matrix3.h4
-rw-r--r--core/math/quat.cpp16
-rw-r--r--core/math/quat.h4
-rw-r--r--core/math/transform.cpp4
-rw-r--r--core/message_queue.cpp2
-rw-r--r--core/method_ptrcall.h45
-rw-r--r--core/node_path.cpp37
-rw-r--r--core/node_path.h15
-rw-r--r--core/object.cpp6
-rw-r--r--core/object.h4
-rw-r--r--core/os/input_event.cpp12
-rw-r--r--core/os/input_event.h5
-rw-r--r--core/os/main_loop.cpp1
-rw-r--r--core/os/main_loop.h1
-rw-r--r--core/os/os.cpp17
-rw-r--r--core/os/os.h10
-rw-r--r--core/project_settings.cpp28
-rw-r--r--core/project_settings.h14
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/resource.cpp17
-rw-r--r--core/script_debugger_remote.cpp9
-rw-r--r--core/script_debugger_remote.h5
-rw-r--r--core/script_language.cpp91
-rw-r--r--core/script_language.h29
-rw-r--r--core/string_db.h6
-rw-r--r--core/type_info.h1
-rw-r--r--core/typedefs.h7
-rw-r--r--core/undo_redo.cpp16
-rw-r--r--core/undo_redo.h4
-rw-r--r--core/ustring.cpp48
-rw-r--r--core/variant.cpp24
-rw-r--r--core/variant.h2
-rw-r--r--core/variant_op.cpp13
-rw-r--r--core/vector.h13
-rw-r--r--doc/classes/@GDScript.xml5
-rw-r--r--doc/classes/Array.xml11
-rw-r--r--doc/classes/ColorPicker.xml3
-rw-r--r--doc/classes/ConfigFile.xml2
-rw-r--r--doc/classes/CylinderShape.xml25
-rw-r--r--doc/classes/EditorPlugin.xml7
-rw-r--r--doc/classes/EditorScenePostImport.xml38
-rw-r--r--doc/classes/Image.xml15
-rw-r--r--doc/classes/Line2D.xml3
-rw-r--r--doc/classes/PhysicsServer.xml11
-rw-r--r--doc/classes/PopupMenu.xml3
-rw-r--r--doc/classes/PrismMesh.xml2
-rw-r--r--doc/classes/ScriptCreateDialog.xml45
-rw-r--r--doc/classes/Slider.xml2
-rw-r--r--doc/classes/SpriteFrames.xml9
-rw-r--r--doc/classes/TextEdit.xml31
-rw-r--r--doc/classes/Texture.xml1
-rw-r--r--doc/classes/TileMap.xml11
-rw-r--r--doc/classes/Vector2.xml1
-rw-r--r--doc/classes/Vector3.xml1
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp90
-rw-r--r--drivers/alsa/audio_driver_alsa.h1
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp89
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h3
-rw-r--r--drivers/dummy/rasterizer_dummy.h9
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.cpp25
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp2
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.cpp2
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.h2
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp22
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h12
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp10
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp38
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp43
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h7
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp156
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h14
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp10
-rw-r--r--drivers/gles3/shaders/canvas.glsl14
-rw-r--r--drivers/gles3/shaders/scene.glsl93
-rw-r--r--drivers/gles3/shaders/tonemap.glsl448
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp20
-rw-r--r--drivers/rtaudio/audio_driver_rtaudio.cpp4
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp68
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h1
-rw-r--r--drivers/windows/file_access_windows.cpp6
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp2
-rw-r--r--editor/SCsub8
-rw-r--r--editor/animation_track_editor.cpp58
-rw-r--r--editor/code_editor.cpp4
-rw-r--r--editor/connections_dialog.cpp20
-rw-r--r--editor/create_dialog.cpp64
-rw-r--r--editor/create_dialog.h1
-rw-r--r--editor/editor_autoload_settings.cpp8
-rw-r--r--editor/editor_export.cpp5
-rw-r--r--editor/editor_file_system.cpp104
-rw-r--r--editor/editor_file_system.h14
-rw-r--r--editor/editor_fonts.cpp17
-rw-r--r--editor/editor_help.cpp3
-rw-r--r--editor/editor_help.h5
-rw-r--r--editor/editor_inspector.cpp282
-rw-r--r--editor/editor_inspector.h47
-rw-r--r--editor/editor_node.cpp359
-rw-r--r--editor/editor_node.h18
-rw-r--r--editor/editor_plugin.cpp9
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/editor_profiler.cpp19
-rw-r--r--editor/editor_profiler.h2
-rw-r--r--editor/editor_properties.cpp335
-rw-r--r--editor/editor_properties.h34
-rw-r--r--editor/editor_properties_array_dict.cpp82
-rw-r--r--editor/editor_properties_array_dict.h2
-rw-r--r--editor/editor_sectioned_inspector.cpp306
-rw-r--r--editor/editor_sectioned_inspector.h42
-rw-r--r--editor/editor_settings.cpp33
-rw-r--r--editor/editor_settings.h7
-rw-r--r--editor/editor_spin_slider.cpp173
-rw-r--r--editor/editor_spin_slider.h14
-rw-r--r--editor/editor_themes.cpp131
-rw-r--r--editor/export_template_manager.cpp21
-rw-r--r--editor/export_template_manager.h2
-rw-r--r--editor/filesystem_dock.cpp98
-rw-r--r--editor/filesystem_dock.h9
-rw-r--r--editor/groups_editor.cpp1
-rw-r--r--editor/icons/README.md4
-rw-r--r--editor/icons/icon_GUI_tree_arrow_up.svg60
-rw-r--r--editor/icons/icon_animated_texture.svg74
-rw-r--r--editor/icons/icon_animation_tree.svg5
-rw-r--r--editor/icons/icon_auto_end.svg68
-rw-r--r--editor/icons/icon_c_p_u_particles.svg60
-rw-r--r--editor/icons/icon_cylinder_shape.svg6
-rw-r--r--editor/icons/icon_expand_bottom_dock.svg70
-rw-r--r--editor/icons/icon_g_l_e_s_2.svg69
-rw-r--r--editor/icons/icon_g_l_e_s_3.svg67
-rw-r--r--editor/icons/icon_new_root.svg69
-rw-r--r--editor/icons/icon_play_travel.svg85
-rw-r--r--editor/icons/icon_script_create_dialog.svg10
-rw-r--r--editor/icons/icon_shrink_bottom_dock.svg71
-rw-r--r--editor/icons/icon_soft_body.svg56
-rw-r--r--editor/icons/icon_tool_add_node.svg80
-rw-r--r--editor/icons/icon_tool_connect.svg72
-rw-r--r--editor/icons/icon_transition_end.svg72
-rw-r--r--editor/icons/icon_transition_end_auto.svg74
-rw-r--r--editor/icons/icon_transition_end_auto_big.svg74
-rw-r--r--editor/icons/icon_transition_end_big.svg74
-rw-r--r--editor/icons/icon_transition_immediate.svg64
-rw-r--r--editor/icons/icon_transition_immediate_auto.svg64
-rw-r--r--editor/icons/icon_transition_immediate_auto_big.svg66
-rw-r--r--editor/icons/icon_transition_immediate_big.svg66
-rw-r--r--editor/icons/icon_transition_sync.svg72
-rw-r--r--editor/icons/icon_transition_sync_auto.svg74
-rw-r--r--editor/icons/icon_transition_sync_auto_big.svg74
-rw-r--r--editor/icons/icon_transition_sync_big.svg74
-rw-r--r--editor/icons/icon_visual_shader.svg105
-rw-r--r--editor/icons/icon_vulkan.svg127
-rw-r--r--editor/import/editor_import_collada.cpp6
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp3
-rw-r--r--editor/import/resource_importer_obj.cpp11
-rw-r--r--editor/import/resource_importer_scene.cpp70
-rw-r--r--editor/import/resource_importer_scene.h6
-rw-r--r--editor/import/resource_importer_texture.cpp4
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp741
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h117
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp (renamed from editor/plugins/animation_blend_space_editor.cpp)132
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h (renamed from editor/plugins/animation_blend_space_editor.h)34
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp33
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp21
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp1313
-rw-r--r--editor/plugins/animation_state_machine_editor.h167
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp284
-rw-r--r--editor/plugins/audio_stream_editor_plugin.h (renamed from modules/theora/resource_importer_theora.cpp)95
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp24
-rw-r--r--editor/plugins/cpu_particles_editor_plugin.cpp114
-rw-r--r--editor/plugins/cpu_particles_editor_plugin.h55
-rw-r--r--editor/plugins/particles_editor_plugin.cpp413
-rw-r--r--editor/plugins/particles_editor_plugin.h34
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp55
-rw-r--r--editor/plugins/path_2d_editor_plugin.h12
-rw-r--r--editor/plugins/path_editor_plugin.cpp65
-rw-r--r--editor/plugins/path_editor_plugin.h19
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp1
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp293
-rw-r--r--editor/plugins/root_motion_editor_plugin.h42
-rw-r--r--editor/plugins/script_editor_plugin.cpp83
-rw-r--r--editor/plugins/script_editor_plugin.h18
-rw-r--r--editor/plugins/script_text_editor.cpp53
-rw-r--r--editor/plugins/script_text_editor.h2
-rw-r--r--editor/plugins/shader_editor_plugin.cpp4
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp26
-rw-r--r--editor/plugins/spatial_editor_plugin.h1
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp272
-rw-r--r--editor/plugins/tile_map_editor_plugin.h13
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp1217
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h187
-rw-r--r--editor/project_export.cpp7
-rw-r--r--editor/project_settings_editor.cpp250
-rw-r--r--editor/project_settings_editor.h17
-rw-r--r--editor/property_editor.cpp1
-rw-r--r--editor/scene_tree_dock.cpp121
-rw-r--r--editor/scene_tree_dock.h22
-rw-r--r--editor/scene_tree_editor.cpp25
-rw-r--r--editor/scene_tree_editor.h3
-rw-r--r--editor/script_create_dialog.cpp3
-rw-r--r--editor/script_editor_debugger.cpp3
-rw-r--r--editor/settings_config_dialog.cpp72
-rw-r--r--editor/settings_config_dialog.h18
-rw-r--r--editor/spatial_editor_gizmos.cpp241
-rw-r--r--editor/spatial_editor_gizmos.h17
-rw-r--r--editor/translations/ar.po6
-rw-r--r--editor/translations/bn.po10
-rw-r--r--editor/translations/ca.po29
-rw-r--r--editor/translations/cs.po10
-rw-r--r--editor/translations/da.po10
-rw-r--r--editor/translations/de.po37
-rw-r--r--editor/translations/el.po10
-rw-r--r--editor/translations/es.po82
-rw-r--r--editor/translations/es_AR.po25
-rw-r--r--editor/translations/fa.po10
-rw-r--r--editor/translations/fi.po918
-rw-r--r--editor/translations/fr.po35
-rw-r--r--editor/translations/he.po8
-rw-r--r--editor/translations/hu.po87
-rw-r--r--editor/translations/id.po21
-rw-r--r--editor/translations/it.po10
-rw-r--r--editor/translations/ja.po105
-rw-r--r--editor/translations/ko.po30
-rw-r--r--editor/translations/lt.po12
-rw-r--r--editor/translations/nb.po98
-rw-r--r--editor/translations/nl.po10
-rw-r--r--editor/translations/pl.po18
-rw-r--r--editor/translations/pt_BR.po32
-rw-r--r--editor/translations/pt_PT.po27
-rw-r--r--editor/translations/ro.po1305
-rw-r--r--editor/translations/ru.po33
-rw-r--r--editor/translations/sk.po53
-rw-r--r--editor/translations/sl.po232
-rw-r--r--editor/translations/sr_Cyrl.po10
-rw-r--r--editor/translations/sv.po11
-rw-r--r--editor/translations/th.po10
-rw-r--r--editor/translations/tr.po35
-rw-r--r--editor/translations/uk.po27
-rw-r--r--editor/translations/vi.po4
-rw-r--r--editor/translations/zh_CN.po43
-rw-r--r--editor/translations/zh_HK.po10
-rw-r--r--editor/translations/zh_TW.po11
-rw-r--r--icon.pngbin11155 -> 7569 bytes
-rw-r--r--logo.pngbin19339 -> 13142 bytes
-rw-r--r--main/SCsub8
-rw-r--r--main/app_icon.pngbin5569 -> 3770 bytes
-rw-r--r--main/main.cpp22
-rw-r--r--main/performance.cpp5
-rw-r--r--main/performance.h1
-rw-r--r--main/splash.pngbin21504 -> 15311 bytes
-rw-r--r--main/splash_editor.pngbin39571 -> 32577 bytes
-rw-r--r--main/tests/test_gdscript.cpp15
-rw-r--r--main/tests/test_shader_lang.cpp4
-rw-r--r--methods.py10
-rwxr-xr-xmisc/dist/appimage/AppRun3
-rw-r--r--misc/dist/appimage/godot.desktop9
-rw-r--r--misc/dist/appimage/godot.pngbin12525 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.pngbin13558 -> 1641 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.pngbin17911 -> 1919 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.pngbin23513 -> 2392 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.pngbin53789 -> 4360 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.pngbin51462 -> 4192 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.pngbin18386 -> 1722 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.pngbin61558 -> 5097 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.pngbin56053 -> 5274 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.pngbin53983 -> 5513 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.pngbin18753 -> 1881 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.pngbin62336 -> 5491 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist4
-rw-r--r--misc/dist/uwp_template/Assets/SplashScreen.scale-100.pngbin14919 -> 10818 bytes
-rw-r--r--misc/dist/uwp_template/Assets/Square150x150Logo.scale-100.pngbin5645 -> 3124 bytes
-rw-r--r--misc/dist/uwp_template/Assets/Square310x310Logo.scale-100.pngbin16169 -> 8939 bytes
-rw-r--r--misc/dist/uwp_template/Assets/Square44x44Logo.scale-100.pngbin1619 -> 848 bytes
-rw-r--r--misc/dist/uwp_template/Assets/Square71x71Logo.scale-100.pngbin2483 -> 1168 bytes
-rw-r--r--misc/dist/uwp_template/Assets/StoreLogo.scale-100.pngbin1792 -> 883 bytes
-rw-r--r--misc/dist/uwp_template/Assets/Wide310x150Logo.scale-100.pngbin6772 -> 3471 bytes
-rwxr-xr-xmisc/travis/clang-format.sh2
-rw-r--r--modules/bmp/image_loader_bmp.cpp2
-rw-r--r--modules/bullet/bullet_physics_server.cpp192
-rw-r--r--modules/bullet/bullet_physics_server.h46
-rw-r--r--modules/bullet/collision_object_bullet.cpp2
-rw-r--r--modules/bullet/register_types.cpp9
-rw-r--r--modules/bullet/rigid_body_bullet.cpp19
-rw-r--r--modules/bullet/rigid_body_bullet.h9
-rw-r--r--modules/bullet/shape_bullet.cpp37
-rw-r--r--modules/bullet/shape_bullet.h20
-rw-r--r--modules/bullet/soft_body_bullet.cpp474
-rw-r--r--modules/bullet/soft_body_bullet.h103
-rw-r--r--modules/bullet/space_bullet.cpp47
-rw-r--r--modules/bullet/space_bullet.h6
-rw-r--r--modules/csg/csg_shape.cpp80
-rw-r--r--modules/csg/csg_shape.h12
-rw-r--r--modules/csg/doc_classes/CSGBox.xml6
-rw-r--r--modules/csg/doc_classes/CSGCombiner.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder.xml8
-rw-r--r--modules/csg/doc_classes/CSGMesh.xml3
-rw-r--r--modules/csg/doc_classes/CSGPolygon.xml27
-rw-r--r--modules/csg/doc_classes/CSGPrimitive.xml2
-rw-r--r--modules/csg/doc_classes/CSGShape.xml8
-rw-r--r--modules/csg/doc_classes/CSGSphere.xml7
-rw-r--r--modules/csg/doc_classes/CSGTorus.xml8
-rw-r--r--modules/gdnative/SCsub5
-rw-r--r--modules/gdnative/config.py1
-rw-r--r--modules/gdnative/include/net/godot_net.h118
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp21
-rw-r--r--modules/gdnative/nativescript/nativescript.h5
-rw-r--r--modules/gdnative/net/SCsub12
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp124
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h77
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.cpp73
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.h (renamed from modules/webm/resource_importer_webm.h)41
-rw-r--r--modules/gdnative/net/register_types.cpp43
-rw-r--r--modules/gdnative/net/register_types.h32
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.cpp (renamed from modules/webm/resource_importer_webm.cpp)71
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.h (renamed from modules/theora/resource_importer_theora.h)47
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h2
-rw-r--r--modules/gdnative/register_types.cpp3
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp30
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h4
-rw-r--r--modules/gdscript/gdscript.cpp105
-rw-r--r--modules/gdscript/gdscript.h13
-rw-r--r--modules/gdscript/gdscript_compiler.cpp665
-rw-r--r--modules/gdscript/gdscript_compiler.h11
-rw-r--r--modules/gdscript/gdscript_editor.cpp4114
-rw-r--r--modules/gdscript/gdscript_function.cpp252
-rw-r--r--modules/gdscript/gdscript_function.h101
-rw-r--r--modules/gdscript/gdscript_functions.cpp71
-rw-r--r--modules/gdscript/gdscript_functions.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp3531
-rw-r--r--modules/gdscript/gdscript_parser.h252
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp17
-rw-r--r--modules/gdscript/gdscript_tokenizer.h4
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp31
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h2
-rw-r--r--modules/jpg/image_loader_jpegd.cpp4
-rwxr-xr-xmodules/mbedtls/SCsub2
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp80
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h6
-rw-r--r--modules/mono/SCsub56
-rw-r--r--modules/mono/config.py126
-rw-r--r--modules/mono/csharp_script.cpp129
-rw-r--r--modules/mono/csharp_script.h5
-rw-r--r--modules/mono/editor/bindings_generator.cpp43
-rw-r--r--modules/mono/editor/csharp_project.cpp32
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp21
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp10
-rw-r--r--modules/mono/editor/monodevelop_instance.cpp16
-rw-r--r--modules/mono/glue/collections_glue.cpp240
-rw-r--r--modules/mono/glue/collections_glue.h100
-rw-r--r--modules/mono/glue/cs_files/Array.cs335
-rw-r--r--modules/mono/glue/cs_files/Basis.cs18
-rw-r--r--modules/mono/glue/cs_files/Dictionary.cs401
-rw-r--r--modules/mono/glue/cs_files/GD.cs35
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs29
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs40
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt2
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/mono_gc_handle.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp47
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp32
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp45
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp171
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp39
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp209
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h62
-rw-r--r--modules/mono/mono_reg_utils.py4
-rw-r--r--modules/mono/signal_awaiter_utils.cpp20
-rw-r--r--modules/mono/tls_configure.py36
-rw-r--r--modules/mono/utils/macros.h59
-rw-r--r--modules/mono/utils/thread_local.cpp99
-rw-r--r--modules/mono/utils/thread_local.h171
-rw-r--r--modules/pvr/texture_loader_pvr.cpp6
-rw-r--r--modules/regex/SCsub3
-rw-r--r--modules/tga/image_loader_tga.cpp3
-rw-r--r--modules/theora/doc_classes/ResourceImporterTheora.xml15
-rw-r--r--modules/theora/register_types.cpp16
-rw-r--r--modules/theora/video_stream_theora.cpp43
-rw-r--r--modules/theora/video_stream_theora.h10
-rw-r--r--modules/visual_script/visual_script.cpp2
-rw-r--r--modules/visual_script/visual_script.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp1
-rw-r--r--modules/visual_script/visual_script_editor.h2
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp2
-rw-r--r--modules/webm/doc_classes/ResourceImporterWebm.xml15
-rw-r--r--modules/webm/register_types.cpp16
-rw-r--r--modules/webm/video_stream_webm.cpp43
-rw-r--r--modules/webm/video_stream_webm.h10
-rw-r--r--modules/webp/image_loader_webp.cpp67
-rw-r--r--modules/websocket/lws_helper.h12
-rw-r--r--platform/android/AndroidManifest.xml.template167
-rw-r--r--platform/android/SCsub5
-rw-r--r--platform/android/audio_driver_jandroid.cpp4
-rw-r--r--platform/android/audio_driver_opensl.cpp3
-rw-r--r--platform/android/audio_driver_opensl.h3
-rw-r--r--platform/android/detect.py9
-rw-r--r--platform/android/export/export.cpp150
-rw-r--r--platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.pngbin715 -> 462 bytes
-rw-r--r--platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.pngbin361 -> 127 bytes
-rw-r--r--platform/android/java/res/drawable/icon.pngbin11155 -> 7569 bytes
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java17
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java2
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java16
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java8
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java1
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java2
-rw-r--r--platform/android/java_glue.cpp16
-rw-r--r--platform/android/logo.pngbin1742 -> 1461 bytes
-rw-r--r--platform/android/os_android.cpp23
-rw-r--r--platform/android/os_android.h7
-rw-r--r--platform/android/run_icon.pngbin636 -> 324 bytes
-rw-r--r--platform/haiku/audio_driver_media_kit.cpp2
-rw-r--r--platform/haiku/logo.pngbin1551 -> 1265 bytes
-rw-r--r--platform/haiku/os_haiku.cpp6
-rw-r--r--platform/haiku/os_haiku.h2
-rw-r--r--platform/iphone/app_delegate.mm2
-rw-r--r--platform/iphone/detect.py8
-rw-r--r--platform/iphone/game_center.mm5
-rw-r--r--platform/iphone/logo.pngbin1905 -> 1489 bytes
-rw-r--r--platform/iphone/os_iphone.cpp15
-rw-r--r--platform/iphone/os_iphone.h14
-rw-r--r--platform/iphone/view_controller.mm65
-rw-r--r--platform/javascript/dom_keys.inc (renamed from platform/javascript/dom_keys.h)17
-rw-r--r--platform/javascript/javascript_main.cpp27
-rw-r--r--platform/javascript/logo.pngbin2316 -> 1236 bytes
-rw-r--r--platform/javascript/os_javascript.cpp1163
-rw-r--r--platform/javascript/os_javascript.h135
-rw-r--r--platform/javascript/run_icon.pngbin471 -> 290 bytes
-rw-r--r--platform/osx/crash_handler_osx.mm4
-rw-r--r--platform/osx/detect.py11
-rw-r--r--platform/osx/logo.pngbin2065 -> 1752 bytes
-rw-r--r--platform/osx/os_osx.h3
-rw-r--r--platform/osx/os_osx.mm34
-rw-r--r--platform/server/logo.pngbin2331 -> 2016 bytes
-rw-r--r--platform/server/os_server.cpp11
-rw-r--r--platform/server/os_server.h9
-rw-r--r--platform/uwp/app.cpp15
-rw-r--r--platform/uwp/gl_context_egl.cpp30
-rw-r--r--platform/uwp/gl_context_egl.h11
-rw-r--r--platform/uwp/os_uwp.cpp50
-rw-r--r--platform/uwp/os_uwp.h9
-rw-r--r--platform/windows/crash_handler_win.cpp3
-rw-r--r--platform/windows/detect.py28
-rw-r--r--platform/windows/godot_win.cpp4
-rw-r--r--platform/windows/logo.pngbin1882 -> 1536 bytes
-rw-r--r--platform/windows/os_windows.cpp386
-rw-r--r--platform/windows/os_windows.h3
-rw-r--r--platform/x11/crash_handler_x11.cpp4
-rw-r--r--platform/x11/detect.py16
-rw-r--r--platform/x11/logo.pngbin2061 -> 1679 bytes
-rw-r--r--platform/x11/os_x11.cpp18
-rw-r--r--platform/x11/os_x11.h3
-rw-r--r--scene/2d/animated_sprite.cpp41
-rw-r--r--scene/2d/animated_sprite.h4
-rw-r--r--scene/2d/audio_stream_player_2d.cpp68
-rw-r--r--scene/2d/audio_stream_player_2d.h6
-rw-r--r--scene/2d/back_buffer_copy.cpp5
-rw-r--r--scene/2d/back_buffer_copy.h1
-rw-r--r--scene/2d/canvas_item.cpp21
-rw-r--r--scene/2d/canvas_item.h39
-rw-r--r--scene/2d/collision_object_2d.cpp25
-rw-r--r--scene/2d/collision_object_2d.h4
-rw-r--r--scene/2d/joints_2d.cpp4
-rw-r--r--scene/2d/light_2d.cpp12
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/line_2d.cpp3
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/line_builder.cpp13
-rw-r--r--scene/2d/node_2d.cpp1
-rw-r--r--scene/2d/physics_body_2d.cpp392
-rw-r--r--scene/2d/physics_body_2d.h41
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp2
-rw-r--r--scene/2d/screen_button.cpp14
-rw-r--r--scene/2d/screen_button.h3
-rw-r--r--scene/2d/sprite.cpp11
-rw-r--r--scene/2d/sprite.h1
-rw-r--r--scene/2d/tile_map.cpp32
-rw-r--r--scene/2d/tile_map.h9
-rw-r--r--scene/3d/arvr_nodes.cpp20
-rw-r--r--scene/3d/audio_stream_player_3d.cpp68
-rw-r--r--scene/3d/audio_stream_player_3d.h6
-rw-r--r--scene/3d/baked_lightmap.cpp1
-rw-r--r--scene/3d/camera.cpp1
-rw-r--r--scene/3d/collision_object.h1
-rw-r--r--scene/3d/collision_shape.h1
-rw-r--r--scene/3d/cpu_particles.cpp1417
-rw-r--r--scene/3d/cpu_particles.h258
-rw-r--r--scene/3d/gi_probe.cpp1
-rw-r--r--scene/3d/light.cpp1
-rw-r--r--scene/3d/mesh_instance.cpp3
-rw-r--r--scene/3d/mesh_instance.h1
-rw-r--r--scene/3d/physics_body.cpp186
-rw-r--r--scene/3d/physics_body.h27
-rw-r--r--scene/3d/physics_joint.cpp8
-rw-r--r--scene/3d/reflection_probe.cpp1
-rw-r--r--scene/3d/remote_transform.cpp2
-rw-r--r--scene/3d/skeleton.cpp8
-rw-r--r--scene/3d/skeleton.h9
-rw-r--r--scene/3d/soft_body.cpp740
-rw-r--r--scene/3d/soft_body.h197
-rw-r--r--scene/3d/spatial.cpp26
-rw-r--r--scene/3d/spatial.h4
-rw-r--r--scene/3d/vehicle_body.cpp2
-rw-r--r--scene/3d/voxel_light_baker.cpp4
-rw-r--r--scene/animation/animation_blend_space_1d.cpp293
-rw-r--r--scene/animation/animation_blend_space_1d.h70
-rw-r--r--scene/animation/animation_blend_space_2d.cpp (renamed from scene/animation/animation_blend_space.cpp)158
-rw-r--r--scene/animation/animation_blend_space_2d.h (renamed from scene/animation/animation_blend_space.h)26
-rw-r--r--scene/animation/animation_blend_tree.cpp106
-rw-r--r--scene/animation/animation_blend_tree.h34
-rw-r--r--scene/animation/animation_node_state_machine.cpp790
-rw-r--r--scene/animation/animation_node_state_machine.h142
-rw-r--r--scene/animation/animation_player.cpp41
-rw-r--r--scene/animation/animation_tree.cpp (renamed from scene/animation/animation_graph_player.cpp)256
-rw-r--r--scene/animation/animation_tree.h (renamed from scene/animation/animation_graph_player.h)38
-rw-r--r--scene/animation/animation_tree_player.cpp2
-rw-r--r--scene/animation/root_motion_view.cpp178
-rw-r--r--scene/animation/root_motion_view.h47
-rw-r--r--scene/audio/audio_player.cpp45
-rw-r--r--scene/audio/audio_player.h5
-rw-r--r--scene/gui/color_picker.cpp30
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/control.cpp229
-rw-r--r--scene/gui/control.h23
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/gui/label.cpp2
-rw-r--r--scene/gui/line_edit.cpp3
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp61
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/gui/rich_text_label.cpp21
-rw-r--r--scene/gui/rich_text_label.h5
-rw-r--r--scene/gui/scroll_bar.cpp6
-rw-r--r--scene/gui/slider.cpp25
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/tab_container.cpp4
-rw-r--r--scene/gui/tab_container.h6
-rw-r--r--scene/gui/text_edit.cpp161
-rw-r--r--scene/gui/text_edit.h12
-rw-r--r--scene/main/http_request.cpp20
-rw-r--r--scene/main/node.cpp30
-rw-r--r--scene/main/node.h9
-rw-r--r--scene/main/scene_tree.cpp31
-rw-r--r--scene/main/scene_tree.h3
-rw-r--r--scene/main/viewport.cpp68
-rw-r--r--scene/main/viewport.h4
-rw-r--r--scene/register_scene_types.cpp82
-rw-r--r--scene/resources/animation.cpp13
-rw-r--r--scene/resources/curve.cpp10
-rw-r--r--scene/resources/curve.h2
-rw-r--r--scene/resources/cylinder_shape.cpp116
-rw-r--r--scene/resources/cylinder_shape.h57
-rw-r--r--scene/resources/default_theme/arrow_down.pngbin184 -> 109 bytes
-rw-r--r--scene/resources/default_theme/arrow_right.pngbin183 -> 103 bytes
-rw-r--r--scene/resources/default_theme/background.pngbin1235 -> 854 bytes
-rw-r--r--scene/resources/default_theme/base_green.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/button_disabled.pngbin486 -> 256 bytes
-rw-r--r--scene/resources/default_theme/button_focus.pngbin418 -> 203 bytes
-rw-r--r--scene/resources/default_theme/button_hover.pngbin606 -> 344 bytes
-rw-r--r--scene/resources/default_theme/button_normal.pngbin598 -> 338 bytes
-rw-r--r--scene/resources/default_theme/checked.pngbin627 -> 363 bytes
-rw-r--r--scene/resources/default_theme/checker_bg.pngbin295 -> 77 bytes
-rw-r--r--scene/resources/default_theme/close.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/close_hl.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/color_picker_sample.pngbin194 -> 117 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp5
-rw-r--r--scene/resources/default_theme/dosfont.pngbin963 -> 683 bytes
-rw-r--r--scene/resources/default_theme/dropdown.pngbin369 -> 133 bytes
-rw-r--r--scene/resources/default_theme/error_icon.pngbin362 -> 111 bytes
-rw-r--r--scene/resources/default_theme/focus.pngbin411 -> 200 bytes
-rw-r--r--scene/resources/default_theme/frame_focus.pngbin448 -> 200 bytes
-rw-r--r--scene/resources/default_theme/full_panel_bg.pngbin430 -> 207 bytes
-rw-r--r--scene/resources/default_theme/graph_node_breakpoint.pngbin277 -> 140 bytes
-rw-r--r--scene/resources/default_theme/graph_node_close.pngbin222 -> 152 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment.pngbin506 -> 382 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment_focus.pngbin482 -> 373 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default.pngbin420 -> 205 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default_focus.pngbin452 -> 195 bytes
-rw-r--r--scene/resources/default_theme/graph_node_position.pngbin278 -> 140 bytes
-rw-r--r--scene/resources/default_theme/graph_node_selected.pngbin933 -> 836 bytes
-rw-r--r--scene/resources/default_theme/graph_port.pngbin273 -> 171 bytes
-rw-r--r--scene/resources/default_theme/hseparator.pngbin323 -> 112 bytes
-rw-r--r--scene/resources/default_theme/hslider_bg.pngbin526 -> 263 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber.pngbin591 -> 300 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_disabled.pngbin386 -> 288 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_hl.pngbin734 -> 450 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.pngbin364 -> 149 bytes
-rw-r--r--scene/resources/default_theme/hsplit_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.pngbin359 -> 97 bytes
-rw-r--r--scene/resources/default_theme/icon_add.pngbin129 -> 86 bytes
-rw-r--r--scene/resources/default_theme/icon_close.pngbin230 -> 155 bytes
-rw-r--r--scene/resources/default_theme/icon_color_pick.pngbin416 -> 227 bytes
-rw-r--r--scene/resources/default_theme/icon_folder.pngbin170 -> 103 bytes
-rw-r--r--scene/resources/default_theme/icon_parent_folder.pngbin329 -> 161 bytes
-rw-r--r--scene/resources/default_theme/icon_play.pngbin237 -> 122 bytes
-rw-r--r--scene/resources/default_theme/icon_reload.pngbin420 -> 234 bytes
-rw-r--r--scene/resources/default_theme/icon_snap_grid.pngbin325 -> 226 bytes
-rw-r--r--scene/resources/default_theme/icon_stop.pngbin312 -> 87 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_less.pngbin112 -> 76 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_more.pngbin129 -> 85 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_reset.pngbin186 -> 108 bytes
-rw-r--r--scene/resources/default_theme/line_edit.pngbin424 -> 176 bytes
-rw-r--r--scene/resources/default_theme/line_edit_disabled.pngbin406 -> 135 bytes
-rw-r--r--scene/resources/default_theme/line_edit_focus.pngbin660 -> 365 bytes
-rw-r--r--scene/resources/default_theme/logo.pngbin8809 -> 6676 bytes
-rwxr-xr-xscene/resources/default_theme/make_header.py26
-rw-r--r--scene/resources/default_theme/mini_checkerboard.pngbin166 -> 80 bytes
-rw-r--r--scene/resources/default_theme/option_arrow.pngbin227 -> 119 bytes
-rw-r--r--scene/resources/default_theme/option_button_disabled.pngbin901 -> 656 bytes
-rw-r--r--scene/resources/default_theme/option_button_focus.pngbin679 -> 416 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover.pngbin924 -> 667 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal.pngbin922 -> 669 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed.pngbin931 -> 677 bytes
-rw-r--r--scene/resources/default_theme/panel_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/popup_bg.pngbin672 -> 410 bytes
-rw-r--r--scene/resources/default_theme/popup_bg_disabled.pngbin582 -> 362 bytes
-rw-r--r--scene/resources/default_theme/popup_checked.pngbin238 -> 143 bytes
-rw-r--r--scene/resources/default_theme/popup_hover.pngbin404 -> 145 bytes
-rw-r--r--scene/resources/default_theme/popup_unchecked.pngbin310 -> 98 bytes
-rw-r--r--scene/resources/default_theme/popup_window.pngbin1234 -> 903 bytes
-rw-r--r--scene/resources/default_theme/progress_bar.pngbin460 -> 194 bytes
-rw-r--r--scene/resources/default_theme/progress_fill.pngbin402 -> 112 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.pngbin477 -> 257 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked.pngbin422 -> 207 bytes
-rw-r--r--scene/resources/default_theme/reference_border.pngbin348 -> 132 bytes
-rw-r--r--scene/resources/default_theme/scroll_bg.pngbin510 -> 252 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_down.pngbin418 -> 170 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_down_hl.pngbin418 -> 170 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.pngbin444 -> 196 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.pngbin461 -> 200 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right.pngbin446 -> 198 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.pngbin464 -> 210 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_up.pngbin421 -> 173 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_up_hl.pngbin421 -> 173 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber.pngbin523 -> 264 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_hl.pngbin536 -> 278 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_pressed.pngbin233 -> 104 bytes
-rw-r--r--scene/resources/default_theme/selection.pngbin406 -> 195 bytes
-rw-r--r--scene/resources/default_theme/selection_oof.pngbin411 -> 196 bytes
-rw-r--r--scene/resources/default_theme/spinbox_updown.pngbin280 -> 146 bytes
-rw-r--r--scene/resources/default_theme/submenu.pngbin175 -> 103 bytes
-rw-r--r--scene/resources/default_theme/tab.pngbin300 -> 82 bytes
-rw-r--r--scene/resources/default_theme/tab_behind.pngbin553 -> 282 bytes
-rw-r--r--scene/resources/default_theme/tab_close.pngbin325 -> 158 bytes
-rw-r--r--scene/resources/default_theme/tab_container_bg.pngbin598 -> 338 bytes
-rw-r--r--scene/resources/default_theme/tab_current.pngbin627 -> 347 bytes
-rw-r--r--scene/resources/default_theme/tab_menu.pngbin186 -> 111 bytes
-rw-r--r--scene/resources/default_theme/tab_menu_hl.pngbin186 -> 111 bytes
-rw-r--r--scene/resources/default_theme/toggle_off.pngbin1355 -> 1077 bytes
-rw-r--r--scene/resources/default_theme/toggle_on.pngbin1318 -> 1034 bytes
-rw-r--r--scene/resources/default_theme/tool_button_pressed.pngbin1230 -> 864 bytes
-rw-r--r--scene/resources/default_theme/tooltip_bg.pngbin424 -> 198 bytes
-rw-r--r--scene/resources/default_theme/tree_bg.pngbin424 -> 176 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_disabled.pngbin406 -> 135 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_focus.pngbin1039 -> 696 bytes
-rw-r--r--scene/resources/default_theme/tree_cursor.pngbin743 -> 434 bytes
-rw-r--r--scene/resources/default_theme/tree_cursor_unfocus.pngbin655 -> 369 bytes
-rw-r--r--scene/resources/default_theme/tree_title.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/tree_title_pressed.pngbin335 -> 86 bytes
-rw-r--r--scene/resources/default_theme/unchecked.pngbin477 -> 222 bytes
-rw-r--r--scene/resources/default_theme/updown.pngbin241 -> 144 bytes
-rw-r--r--scene/resources/default_theme/vseparator.pngbin323 -> 108 bytes
-rw-r--r--scene/resources/default_theme/vslider_bg.pngbin532 -> 276 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber.pngbin394 -> 245 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_disabled.pngbin335 -> 237 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_hl.pngbin439 -> 261 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.pngbin360 -> 145 bytes
-rw-r--r--scene/resources/default_theme/vsplit_bg.pngbin334 -> 85 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.pngbin351 -> 95 bytes
-rw-r--r--scene/resources/default_theme/window_resizer.pngbin181 -> 87 bytes
-rw-r--r--scene/resources/dynamic_font.cpp2
-rw-r--r--scene/resources/environment.cpp37
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/material.cpp23
-rw-r--r--scene/resources/material.h6
-rw-r--r--scene/resources/mesh.cpp53
-rw-r--r--scene/resources/mesh.h6
-rw-r--r--scene/resources/physics_material.cpp94
-rw-r--r--scene/resources/physics_material.h70
-rw-r--r--scene/resources/scene_format_text.cpp2
-rw-r--r--scene/resources/shader.cpp18
-rw-r--r--scene/resources/shader.h3
-rw-r--r--scene/resources/style_box.cpp36
-rw-r--r--scene/resources/style_box.h10
-rw-r--r--scene/resources/surface_tool.cpp1
-rw-r--r--scene/resources/texture.cpp205
-rw-r--r--scene/resources/texture.h69
-rw-r--r--scene/resources/theme.h12
-rw-r--r--scene/resources/visual_shader.cpp1555
-rw-r--r--scene/resources/visual_shader.h284
-rw-r--r--scene/resources/visual_shader_nodes.cpp1896
-rw-r--r--scene/resources/visual_shader_nodes.h861
-rw-r--r--servers/audio/audio_driver_dummy.cpp2
-rw-r--r--servers/audio_server.cpp120
-rw-r--r--servers/audio_server.h31
-rw-r--r--servers/physics/body_pair_sw.cpp42
-rw-r--r--servers/physics/body_sw.cpp16
-rw-r--r--servers/physics/body_sw.h8
-rw-r--r--servers/physics/physics_server_sw.cpp25
-rw-r--r--servers/physics/physics_server_sw.h70
-rw-r--r--servers/physics_2d/body_2d_sw.cpp16
-rw-r--r--servers/physics_2d/body_2d_sw.h8
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp42
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.cpp11
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.h2
-rw-r--r--servers/physics_2d/physics_2d_server_sw.cpp40
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h7
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h13
-rw-r--r--servers/physics_2d/space_2d_sw.cpp163
-rw-r--r--servers/physics_2d/space_2d_sw.h3
-rw-r--r--servers/physics_2d_server.h30
-rw-r--r--servers/physics_server.cpp5
-rw-r--r--servers/physics_server.h80
-rw-r--r--servers/server_wrap_mt_common.h9
-rw-r--r--servers/visual/rasterizer.h15
-rw-r--r--servers/visual/shader_language.cpp20
-rw-r--r--servers/visual/shader_language.h6
-rw-r--r--servers/visual/shader_types.cpp83
-rw-r--r--servers/visual/shader_types.h6
-rw-r--r--servers/visual/visual_server_raster.cpp5
-rw-r--r--servers/visual/visual_server_raster.h12
-rw-r--r--servers/visual/visual_server_scene.cpp57
-rw-r--r--servers/visual/visual_server_viewport.cpp2
-rw-r--r--servers/visual/visual_server_wrap_mt.h10
-rw-r--r--servers/visual_server.cpp165
-rw-r--r--servers/visual_server.h23
-rw-r--r--thirdparty/README.md11
-rw-r--r--thirdparty/mbedtls/1453.diff24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/aes.h91
-rw-r--r--thirdparty/mbedtls/include/mbedtls/arc4.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/aria.h331
-rw-r--r--thirdparty/mbedtls/include/mbedtls/bignum.h10
-rw-r--r--thirdparty/mbedtls/include/mbedtls/blowfish.h57
-rw-r--r--thirdparty/mbedtls/include/mbedtls/bn_mul.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/camellia.h70
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ccm.h54
-rw-r--r--thirdparty/mbedtls/include/mbedtls/cipher.h338
-rw-r--r--thirdparty/mbedtls/include/mbedtls/cmac.h54
-rw-r--r--thirdparty/mbedtls/include/mbedtls/config.h126
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ctr_drbg.h58
-rw-r--r--thirdparty/mbedtls/include/mbedtls/des.h23
-rw-r--r--thirdparty/mbedtls/include/mbedtls/dhm.h130
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecdh.h113
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecdsa.h181
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecjpake.h20
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecp.h712
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecp_internal.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/entropy.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/error.h3
-rw-r--r--thirdparty/mbedtls/include/mbedtls/gcm.h81
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md.h106
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md2.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md4.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md5.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/net_sockets.h33
-rw-r--r--thirdparty/mbedtls/include/mbedtls/pk.h12
-rw-r--r--thirdparty/mbedtls/include/mbedtls/platform.h69
-rw-r--r--thirdparty/mbedtls/include/mbedtls/platform_util.h62
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ripemd160.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/rsa.h675
-rw-r--r--thirdparty/mbedtls/include/mbedtls/rsa_internal.h60
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha1.h162
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha256.h70
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha512.h62
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl.h209
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h41
-rw-r--r--thirdparty/mbedtls/include/mbedtls/threading.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/timing.h24
-rw-r--r--thirdparty/mbedtls/include/mbedtls/version.h8
-rw-r--r--thirdparty/mbedtls/include/mbedtls/x509_crt.h9
-rw-r--r--thirdparty/mbedtls/include/mbedtls/xtea.h24
-rw-r--r--thirdparty/mbedtls/library/aes.c151
-rw-r--r--thirdparty/mbedtls/library/aesni.c6
-rw-r--r--thirdparty/mbedtls/library/arc4.c8
-rw-r--r--thirdparty/mbedtls/library/aria.c1028
-rw-r--r--thirdparty/mbedtls/library/asn1parse.c10
-rw-r--r--thirdparty/mbedtls/library/asn1write.c4
-rw-r--r--thirdparty/mbedtls/library/bignum.c149
-rw-r--r--thirdparty/mbedtls/library/blowfish.c8
-rw-r--r--thirdparty/mbedtls/library/camellia.c8
-rw-r--r--thirdparty/mbedtls/library/ccm.c10
-rw-r--r--thirdparty/mbedtls/library/cipher.c25
-rw-r--r--thirdparty/mbedtls/library/cipher_wrap.c393
-rw-r--r--thirdparty/mbedtls/library/cmac.c40
-rw-r--r--thirdparty/mbedtls/library/ctr_drbg.c22
-rw-r--r--thirdparty/mbedtls/library/des.c18
-rw-r--r--thirdparty/mbedtls/library/dhm.c11
-rw-r--r--thirdparty/mbedtls/library/ecdsa.c3
-rw-r--r--thirdparty/mbedtls/library/ecjpake.c2
-rw-r--r--thirdparty/mbedtls/library/ecp.c32
-rw-r--r--thirdparty/mbedtls/library/ecp_curves.c135
-rw-r--r--thirdparty/mbedtls/library/entropy.c18
-rw-r--r--thirdparty/mbedtls/library/entropy_poll.c2
-rw-r--r--thirdparty/mbedtls/library/error.c35
-rw-r--r--thirdparty/mbedtls/library/gcm.c10
-rw-r--r--thirdparty/mbedtls/library/havege.c8
-rw-r--r--thirdparty/mbedtls/library/hmac_drbg.c12
-rw-r--r--thirdparty/mbedtls/library/md.c15
-rw-r--r--thirdparty/mbedtls/library/md2.c8
-rw-r--r--thirdparty/mbedtls/library/md4.c8
-rw-r--r--thirdparty/mbedtls/library/md5.c8
-rw-r--r--thirdparty/mbedtls/library/memory_buffer_alloc.c8
-rw-r--r--thirdparty/mbedtls/library/net_sockets.c78
-rw-r--r--thirdparty/mbedtls/library/pem.c28
-rw-r--r--thirdparty/mbedtls/library/pk.c9
-rw-r--r--thirdparty/mbedtls/library/pk_wrap.c18
-rw-r--r--thirdparty/mbedtls/library/pkcs12.c20
-rw-r--r--thirdparty/mbedtls/library/pkcs5.c20
-rw-r--r--thirdparty/mbedtls/library/pkparse.c20
-rw-r--r--thirdparty/mbedtls/library/platform.c11
-rw-r--r--thirdparty/mbedtls/library/platform_util.c67
-rw-r--r--thirdparty/mbedtls/library/ripemd160.c8
-rw-r--r--thirdparty/mbedtls/library/rsa.c70
-rw-r--r--thirdparty/mbedtls/library/sha1.c8
-rw-r--r--thirdparty/mbedtls/library/sha256.c8
-rw-r--r--thirdparty/mbedtls/library/sha512.c8
-rw-r--r--thirdparty/mbedtls/library/ssl_ciphersuites.c407
-rw-r--r--thirdparty/mbedtls/library/ssl_cli.c166
-rw-r--r--thirdparty/mbedtls/library/ssl_cookie.c10
-rw-r--r--thirdparty/mbedtls/library/ssl_srv.c26
-rw-r--r--thirdparty/mbedtls/library/ssl_ticket.c10
-rw-r--r--thirdparty/mbedtls/library/ssl_tls.c493
-rw-r--r--thirdparty/mbedtls/library/threading.c12
-rw-r--r--thirdparty/mbedtls/library/timing.c2
-rw-r--r--thirdparty/mbedtls/library/version_features.c12
-rw-r--r--thirdparty/mbedtls/library/x509_crl.c17
-rw-r--r--thirdparty/mbedtls/library/x509_crt.c728
-rw-r--r--thirdparty/mbedtls/library/x509_csr.c14
-rw-r--r--thirdparty/mbedtls/library/x509write_crt.c8
-rw-r--r--thirdparty/mbedtls/library/x509write_csr.c8
-rw-r--r--thirdparty/mbedtls/library/xtea.c8
-rw-r--r--thirdparty/pcre2/AUTHORS6
-rw-r--r--thirdparty/pcre2/LICENCE27
-rw-r--r--thirdparty/pcre2/src/config.h57
-rw-r--r--thirdparty/pcre2/src/pcre2.h215
-rw-r--r--thirdparty/pcre2/src/pcre2_auto_possess.c66
-rw-r--r--thirdparty/pcre2/src/pcre2_compile.c794
-rw-r--r--thirdparty/pcre2/src/pcre2_config.c48
-rw-r--r--thirdparty/pcre2/src/pcre2_context.c119
-rw-r--r--thirdparty/pcre2/src/pcre2_convert.c1176
-rw-r--r--thirdparty/pcre2/src/pcre2_dfa_match.c529
-rw-r--r--thirdparty/pcre2/src/pcre2_error.c14
-rw-r--r--thirdparty/pcre2/src/pcre2_extuni.c148
-rw-r--r--thirdparty/pcre2/src/pcre2_internal.h132
-rw-r--r--thirdparty/pcre2/src/pcre2_intmodedep.h165
-rw-r--r--thirdparty/pcre2/src/pcre2_jit_compile.c3846
-rw-r--r--thirdparty/pcre2/src/pcre2_jit_match.c10
-rw-r--r--thirdparty/pcre2/src/pcre2_jit_test.c1735
-rw-r--r--thirdparty/pcre2/src/pcre2_match.c8330
-rw-r--r--thirdparty/pcre2/src/pcre2_match_data.c6
-rw-r--r--thirdparty/pcre2/src/pcre2_pattern_info.c37
-rw-r--r--thirdparty/pcre2/src/pcre2_printint.c832
-rw-r--r--thirdparty/pcre2/src/pcre2_serialize.c5
-rw-r--r--thirdparty/pcre2/src/pcre2_study.c38
-rw-r--r--thirdparty/pcre2/src/pcre2_substring.c9
-rw-r--r--thirdparty/pcre2/src/pcre2_tables.c448
-rw-r--r--thirdparty/pcre2/src/pcre2_ucd.c5447
-rw-r--r--thirdparty/pcre2/src/pcre2_ucp.h38
-rw-r--r--thirdparty/pcre2/src/pcre2_valid_utf.c8
-rw-r--r--thirdparty/pcre2/src/sljit/sljitConfig.h8
-rw-r--r--thirdparty/pcre2/src/sljit/sljitConfigInternal.h98
-rw-r--r--thirdparty/pcre2/src/sljit/sljitExecAllocator.c2
-rw-r--r--thirdparty/pcre2/src/sljit/sljitLir.c1160
-rw-r--r--thirdparty/pcre2/src/sljit/sljitLir.h844
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeARM_32.c1613
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeARM_64.c888
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c1198
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c482
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c381
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c776
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativePPC_32.c101
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativePPC_64.c201
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativePPC_common.c1358
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c132
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c340
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeTILEGX-encoder.c2
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeTILEGX_64.c30
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeX86_32.c519
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeX86_64.c351
-rw-r--r--thirdparty/pcre2/src/sljit/sljitNativeX86_common.c792
-rw-r--r--thirdparty/pcre2/src/sljit/sljitProtExecAllocator.c421
-rw-r--r--thirdparty/pcre2/src/sljit/sljitUtils.c116
917 files changed, 63451 insertions, 28627 deletions
diff --git a/.clang-format b/.clang-format
index 212bc25109..3a2c39a174 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,6 +1,6 @@
# Commented out parameters are those with the same value as base LLVM style
# We can uncomment them if we want to change their value, or enforce the
-# chosen value in case the base style changes (last sync: Clang 5.0.0).
+# chosen value in case the base style changes (last sync: Clang 6.0.1).
---
### General config, applies to all languages ###
BasedOnStyle: LLVM
@@ -32,6 +32,7 @@ AllowShortIfStatementsOnASingleLine: true
# AfterObjCDeclaration: false
# AfterStruct: false
# AfterUnion: false
+# AfterExternBlock: false
# BeforeCatch: false
# BeforeElse: false
# IndentBraces: false
@@ -60,6 +61,7 @@ Cpp11BracedListStyle: false
# - foreach
# - Q_FOREACH
# - BOOST_FOREACH
+# IncludeBlocks: Preserve
IncludeCategories:
- Regex: '".*"'
Priority: 1
@@ -69,6 +71,7 @@ IncludeCategories:
Priority: 3
# IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
+# IndentPPDirectives: None
IndentWidth: 4
# IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
@@ -86,6 +89,10 @@ IndentWidth: 4
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
# PointerAlignment: Right
+# RawStringFormats:
+# - Delimiter: pb
+# Language: TextProto
+# BasedOnStyle: google
# ReflowComments: true
# SortIncludes: true
# SortUsingDeclarations: true
diff --git a/.editorconfig b/.editorconfig
index e51d0d4c60..d4b7fbb471 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,7 +9,7 @@ insert_final_newline = true
[*.{cpp,hpp,c,h,mm}]
trim_trailing_whitespace = true
-[*.py]
+[*.{py,cs}]
indent_style = space
indent_size = 4
diff --git a/.travis.yml b/.travis.yml
index 8c24291328..133a134758 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,9 +23,11 @@ matrix:
addons:
apt:
sources:
- - llvm-toolchain-trusty-5.0
+ - llvm-toolchain-trusty-6.0
+ - ubuntu-toolchain-r-test
packages:
- - clang-format-5.0
+ - clang-format-6.0
+ - libstdc++6 # >= 4.9 needed for clang-format-6.0
coverity_scan:
project:
diff --git a/AUTHORS.md b/AUTHORS.md
index 12494a487d..d3f0592c89 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -40,14 +40,15 @@ name is available.
Ben Brookshire (sheepandshepherd)
Benjamin (Nallebeorn)
Bernard Liebl (poke1024)
- Bojidar Marinov (bojidar-bg)
Błażej Szczygieł (zaps166)
+ Bojidar Marinov (bojidar-bg)
+ bruvzg
Carl Olsson (not-surt)
Dana Olson (adolson)
Daniel J. Ramirez (djrm)
Dmitry Koteroff (Krakean)
- Дмитрий Сальников (DmitriySalnikov)
Emmanuel Leblond (touilleMan)
+ est31
Fabio Alessandrelli (Faless)
Ferenc Arn (tagcup)
Franklin Sobrinho (TheHX)
@@ -68,6 +69,7 @@ name is available.
J08nY
Jakub Grzesik (kubecz3k)
Jérôme GULLY (Nutriz)
+ JFonS
Johan Manuel (29jm)
Joshua Grams (JoshuaGrams)
Juan Linietsky (reduz)
@@ -76,15 +78,18 @@ name is available.
Kelly Thomas (KellyThomas)
Kostadin Damyanov (Max-Might)
Leon Krause (eska014)
- Marc Gilleron (Zylann)
+ m4nu3lf
Marcelo Fernandez (marcelofg55)
+ Marc Gilleron (Zylann)
Mariano Javier Suligoy (MarianoGnu)
Mario Schlack (hurikhan)
Martin Sjursen (binbitten)
+ marynate
Masoud BH (masoudbh3)
Matthias Hölzl (hoelzl)
Max Hilbrunner (mhilbrunner)
Michael Alexsander Silva Dias (YeldhamDev)
+ mrezai
Nathan Warden (NathanWarden)
Nuno Donato (nunodonato)
Ovnuniarchos
@@ -102,28 +107,24 @@ name is available.
Ray Koopa (RayKoopa)
Rémi Verschelde (akien-mga)
Roberto F. Arroyo (robfram)
+ romulox-x
+ rraallvv
Ruslan Mustakov (endragor)
Saniko (sanikoyes)
SaracenOne
+ sersoong
Theo Hallenius (TheoXD)
Thomas Herzog (karroffel)
Timo (toger5)
- V. Vamsi Krishna (vkbsb)
Vinzenz Feenstra (vinzenz)
+ 박한얼 (volzhs)
+ V. Vamsi Krishna (vkbsb)
Wilhem Barbier (nounoursheureux)
Will Nations (willnationsdev)
Wilson E. Alvarez (Rubonnek)
Xavier Cho (mysticfall)
+ yg2f (SuperUserNameMan)
Yuri Roubinski (Chaosus)
Zher Huei Lee (leezh)
ZuBsPaCe
- 박한얼 (volzhs)
- bruvzg
- est31
- m4nu3lf
- marynate
- mrezai
- romulox-x
- rraallvv
- sersoong
- yg2f (SuperUserNameMan)
+ Дмитрий Сальников (DmitriySalnikov)
diff --git a/CODEOWNERS b/CODEOWNERS
index 30d80990a6..27315f2c9d 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -17,7 +17,7 @@ misc/* @akien-mga
modules/bullet/* @AndreaCatania
modules/enet/* @godotengine/network
-modules/gnative/* @karroffel
+modules/gdnative/* @karroffel
modules/gdscript/* @reduz @vnen @bojidar-bg
modules/mbedtls/* @godotengine/network
modules/mobile_vr/* @BastiaanOlij
diff --git a/DONORS.md b/DONORS.md
index da0f5bca75..cb7f6e7471 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -70,6 +70,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Johannes Wuensch
Josep G. Camarasa
Joshua Lesperance
+ Kyle Szklenski
Libre-Dépanne
Matthew Bennett
Olafur Gislason
@@ -87,21 +88,21 @@ generous deed immortalized in the next stable release of Godot Engine.
David Churchill
Dean Harmon
Dexter Miguel
- Guilherme Felipe de C. G. da Silva
John
Justo Delgado Baudí
KTL
Laurence Bannister
+ paul gruenbacher
Rami
Robert Willes
Robin Arys
+ Rodrigo Loli
Ronnie Ashlock
Rufus Xavier Sarsaparilla
ScottMakesGames
Thomas Bjarnelöf
William Connell
Wojciech Chojnacki
- Xavier Tan
Zaq Poi
Alessandra Pereyra
@@ -113,13 +114,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Cody Parker
Corey Auger
D
- Daniel Eliasinski
E.G.
+ Eric
Eric Monson
flesk
+ floopf
G Barnes
GGGames.org
Giovanni Solimeno
+ Guilherme Felipe de C. G. da Silva
Hasen Judy
Heath Hayes
Jay Horton
@@ -133,31 +136,32 @@ generous deed immortalized in the next stable release of Godot Engine.
Markus Wiesner
Marvin
Mohammad Taleb
+ Neal Barry
Nick Nikitin
Pablo Cholaky
Patrick Schnorbus
Pete Goodwin
Phyronnaz
Ruben Soares Luis
+ Sindre Sømme
Sofox
Stoned Xander
- Ted
Tim Dalporto
Trent McPheron
- Vladimir
## Silver donors
1D_Inc
Adam Carr
Adam Smeltzer
+ Adisibio
Alder Stefano
Alessandro Senese
- Ãlvaro Domínguez López
Anders Jensen-Urstad
Anthony Bongiovanni
Arda Erol
Arthur S. Muszynski
+ Artur Barichello
Aubrey Falconer
Avencherus
Bailey
@@ -168,6 +172,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Blair Allen
Brandon
Bryan Stevenson
+ Carl Winder
Carwyn Edwards
Casey Foote
Chris Chapin
@@ -175,7 +180,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Christian Winter
Christopher Schmitt
Collin Shooltz
- Daniel Delgado Corona
Daniel Johnson
Daniel Kaplan
DanielMaximiano
@@ -186,6 +190,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Dominik Wetzel
Duy Kevin Nguyen
Edward Herbert
+ Edwin Acosta
Eric Martini
Fabian Becker
fengjiongmax
@@ -194,10 +199,10 @@ generous deed immortalized in the next stable release of Godot Engine.
Gerrit Großkopf
Gerrit Procee
Gilberto K. Otubo
+ Guillaume Laforte
Guldoman
Gumichan01
Heribert Hirth
- hubert jenkins
Hunter Jones
ialex32x
Ivan Vodopiviz
@@ -222,16 +227,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Kevin Kamper Meejach Petersen
Klavdij Voncina
Krzysztof Jankowski
- Lars pfeffer
Linus Lind Lundgren
Luis Moraes
Macil
magodev
Martin Eigel
Martins Odabi
- Matthew Fitzpatrick
Max R.R. Collada
Maxwell
+ Mertcan Mermerkaya
mhilbrunner
Michael Dürwald
Michael Gringauz
@@ -246,10 +250,8 @@ generous deed immortalized in the next stable release of Godot Engine.
Niclas Eriksen
Nicolas SAN AGUSTIN
Niko Leopold
- nivardus
Noi Sek
Oleg Tyshchenko
- Oleksandr Yemets
Pablo Seibelt
Pan Ip
Pat LaBine
@@ -260,28 +262,28 @@ generous deed immortalized in the next stable release of Godot Engine.
Pierre-Igor Berthet
Pietro Vertechi
Piotr Kaczmarski
- Rea
Rémi Verschelde
Richman Stewart
Roger Burgess
Roger Smith
Roman Tinkov
Ryan Whited
- Samuel El-Borai
Sasori Olkof
Sootstone
Stefan Butucea
Theo Cranmore
Thibault Barbaroux
Thomas Bell
+ Thomas Hermansen
+ Thomas Holmes
Thomas Kurz
- Tomasz Wacławek
Tom Larrow
Tyler Stafos
UltyX
Victor
Victor Gonzalez Fernandez
Viktor Ferenczi
+ waka nya
werner mendizabal
Wout Standaert
Yu He
diff --git a/SConstruct b/SConstruct
index df0e40381f..08c5b6d34f 100644
--- a/SConstruct
+++ b/SConstruct
@@ -74,6 +74,7 @@ env_base.android_gradle_plugins = []
env_base.android_gradle_classpath = []
env_base.android_java_dirs = []
env_base.android_res_dirs = []
+env_base.android_asset_dirs = []
env_base.android_aidl_dirs = []
env_base.android_jni_dirs = []
env_base.android_default_config = []
@@ -99,6 +100,7 @@ env_base.__class__.android_add_flat_dir = methods.android_add_flat_dir
env_base.__class__.android_add_dependency = methods.android_add_dependency
env_base.__class__.android_add_java_dir = methods.android_add_java_dir
env_base.__class__.android_add_res_dir = methods.android_add_res_dir
+env_base.__class__.android_add_asset_dir = methods.android_add_asset_dir
env_base.__class__.android_add_aidl_dir = methods.android_add_aidl_dir
env_base.__class__.android_add_jni_dir = methods.android_add_jni_dir
env_base.__class__.android_add_default_config = methods.android_add_default_config
@@ -118,6 +120,7 @@ env_base.__class__.split_lib = methods.split_lib
env_base.__class__.add_shared_library = methods.add_shared_library
env_base.__class__.add_library = methods.add_library
env_base.__class__.add_program = methods.add_program
+env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base["x86_libtheora_opt_gcc"] = False
env_base["x86_libtheora_opt_vc"] = False
@@ -142,6 +145,7 @@ opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32
opts.Add('p', "Platform (alias for 'platform')", '')
opts.Add('platform', "Target platform (%s)" % ('|'.join(platform_list), ), '')
opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release_debug', 'release')))
+opts.Add(EnumVariable('optimize', "Optimization type", 'speed', ('speed', 'size')))
opts.Add(BoolVariable('tools', "Build the tools (a.k.a. the Godot editor)", True))
opts.Add(BoolVariable('use_lto', 'Use link-time optimization', False))
@@ -413,11 +417,19 @@ if selected_platform in platform_list:
if env['tools']:
env.Append(CPPDEFINES=['TOOLS_ENABLED'])
if env['disable_3d']:
- env.Append(CPPDEFINES=['_3D_DISABLED'])
+ if env['tools']:
+ print("Build option 'disable_3d=yes' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).")
+ sys.exit(255)
+ else:
+ env.Append(CPPDEFINES=['_3D_DISABLED'])
if env['gdscript']:
env.Append(CPPDEFINES=['GDSCRIPT_ENABLED'])
if env['disable_advanced_gui']:
- env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED'])
+ if env['tools']:
+ print("Build option 'disable_advanced_gui=yes' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).")
+ sys.exit(255)
+ else:
+ env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED'])
if env['minizip']:
env.Append(CPPDEFINES=['MINIZIP_ENABLED'])
if env['xml']:
diff --git a/core/SCsub b/core/SCsub
index c4f1cdbe97..c508ecc37e 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -93,19 +93,19 @@ env.add_source_files(env.core_sources, "*.cpp")
# Make binders
import make_binders
-env.Command(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run)
+env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run)
# Authors
env.Depends('#core/authors.gen.h', "../AUTHORS.md")
-env.Command('#core/authors.gen.h', "../AUTHORS.md", methods.make_authors_header)
+env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", methods.make_authors_header)
# Donors
env.Depends('#core/donors.gen.h', "../DONORS.md")
-env.Command('#core/donors.gen.h', "../DONORS.md", methods.make_donors_header)
+env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", methods.make_donors_header)
# License
env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"])
-env.Command('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], methods.make_license_header)
+env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], methods.make_license_header)
# Chain load SCsubs
SConscript('os/SCsub')
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 59b100e282..f97eaf6099 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -248,9 +248,9 @@ void ClassDB::set_current_api(APIType p_api) {
current_api = p_api;
}
-HashMap<StringName, ClassDB::ClassInfo, StringNameHasher> ClassDB::classes;
-HashMap<StringName, StringName, StringNameHasher> ClassDB::resource_base_extensions;
-HashMap<StringName, StringName, StringNameHasher> ClassDB::compat_classes;
+HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes;
+HashMap<StringName, StringName> ClassDB::resource_base_extensions;
+HashMap<StringName, StringName> ClassDB::compat_classes;
ClassDB::ClassInfo::ClassInfo() {
diff --git a/core/class_db.h b/core/class_db.h
index 2c77ffe65f..f1d1879236 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -114,10 +114,10 @@ public:
APIType api;
ClassInfo *inherits_ptr;
- HashMap<StringName, MethodBind *, StringNameHasher> method_map;
- HashMap<StringName, int, StringNameHasher> constant_map;
+ HashMap<StringName, MethodBind *> method_map;
+ HashMap<StringName, int> constant_map;
HashMap<StringName, List<StringName> > enum_map;
- HashMap<StringName, MethodInfo, StringNameHasher> signal_map;
+ HashMap<StringName, MethodInfo> signal_map;
List<PropertyInfo> property_list;
#ifdef DEBUG_METHODS_ENABLED
List<StringName> constant_order;
@@ -126,7 +126,7 @@ public:
List<MethodInfo> virtual_methods;
StringName category;
#endif
- HashMap<StringName, PropertySetGet, StringNameHasher> property_setget;
+ HashMap<StringName, PropertySetGet> property_setget;
StringName inherits;
StringName name;
@@ -143,9 +143,9 @@ public:
}
static RWLock *lock;
- static HashMap<StringName, ClassInfo, StringNameHasher> classes;
- static HashMap<StringName, StringName, StringNameHasher> resource_base_extensions;
- static HashMap<StringName, StringName, StringNameHasher> compat_classes;
+ static HashMap<StringName, ClassInfo> classes;
+ static HashMap<StringName, StringName> resource_base_extensions;
+ static HashMap<StringName, StringName> compat_classes;
#ifdef DEBUG_METHODS_ENABLED
static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount);
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index 3942b961d3..7978eaa7bf 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -54,9 +54,13 @@
#define _COMMA_10 ,
#define _COMMA_11 ,
#define _COMMA_12 ,
+#define _COMMA_13 ,
// 1-based comma separated list of ITEMs
#define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM)
+#define _COMMA_SEP_LIST_13(ITEM) \
+ _COMMA_SEP_LIST_12(ITEM) \
+ , ITEM(13)
#define _COMMA_SEP_LIST_12(ITEM) \
_COMMA_SEP_LIST_11(ITEM) \
, ITEM(12)
@@ -97,6 +101,9 @@
// 1-based semicolon separated list of ITEMs
#define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM)
+#define _SEMIC_SEP_LIST_13(ITEM) \
+ _SEMIC_SEP_LIST_12(ITEM); \
+ ITEM(13)
#define _SEMIC_SEP_LIST_12(ITEM) \
_SEMIC_SEP_LIST_11(ITEM); \
ITEM(12)
@@ -137,6 +144,9 @@
// 1-based space separated list of ITEMs
#define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM)
+#define _SPACE_SEP_LIST_13(ITEM) \
+ _SPACE_SEP_LIST_12(ITEM) \
+ ITEM(13)
#define _SPACE_SEP_LIST_12(ITEM) \
_SPACE_SEP_LIST_11(ITEM) \
ITEM(12)
@@ -262,7 +272,7 @@
ss->sem->wait(); \
}
-#define MAX_CMD_PARAMS 12
+#define MAX_CMD_PARAMS 13
class CommandQueueMT {
@@ -290,15 +300,15 @@ class CommandQueueMT {
};
DECL_CMD(0)
- SPACE_SEP_LIST(DECL_CMD, 12)
+ SPACE_SEP_LIST(DECL_CMD, 13)
/* comands that return */
DECL_CMD_RET(0)
- SPACE_SEP_LIST(DECL_CMD_RET, 12)
+ SPACE_SEP_LIST(DECL_CMD_RET, 13)
/* commands that don't return but sync */
DECL_CMD_SYNC(0)
- SPACE_SEP_LIST(DECL_CMD_SYNC, 12)
+ SPACE_SEP_LIST(DECL_CMD_SYNC, 13)
/***** BASE *******/
@@ -432,15 +442,15 @@ class CommandQueueMT {
public:
/* NORMAL PUSH COMMANDS */
DECL_PUSH(0)
- SPACE_SEP_LIST(DECL_PUSH, 12)
+ SPACE_SEP_LIST(DECL_PUSH, 13)
/* PUSH AND RET COMMANDS */
DECL_PUSH_AND_RET(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_RET, 12)
+ SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13)
/* PUSH AND RET SYNC COMMANDS*/
DECL_PUSH_AND_SYNC(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 12)
+ SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13)
void wait_and_flush_one() {
ERR_FAIL_COND(!sync);
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index d68411a572..42d9eab310 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -140,6 +140,11 @@ void Dictionary::erase(const Variant &p_key) {
_p->variant_map.erase(p_key);
}
+bool Dictionary::erase_checked(const Variant &p_key) {
+
+ return _p->variant_map.erase(p_key);
+}
+
bool Dictionary::operator==(const Dictionary &p_dictionary) const {
return _p == p_dictionary._p;
diff --git a/core/dictionary.h b/core/dictionary.h
index 84a5cafe1d..00ec67fb99 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -66,6 +66,7 @@ public:
bool has_all(const Array &p_keys) const;
void erase(const Variant &p_key);
+ bool erase_checked(const Variant &p_key);
bool operator==(const Dictionary &p_dictionary) const;
diff --git a/core/dvector.h b/core/dvector.h
index c0190fb9e3..e03a755e6c 100644
--- a/core/dvector.h
+++ b/core/dvector.h
@@ -150,7 +150,7 @@ class PoolVector {
}
if (old_alloc->refcount.unref() == true) {
- //this should never happen but..
+ //this should never happen but..
#ifdef DEBUG_ENABLED
MemoryPool::alloc_mutex->lock();
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index 04810afe73..5b4dd05dbf 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -378,6 +378,8 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_LEFT);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_RIGHT);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MIDDLE);
+ BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON1);
+ BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON2);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_UP);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_DOWN);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_LEFT);
@@ -385,6 +387,8 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_LEFT);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_RIGHT);
BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_MIDDLE);
+ BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON1);
+ BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2);
//joypads
BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0);
diff --git a/core/hashfuncs.h b/core/hashfuncs.h
index ae99fa39c8..735e679d1e 100644
--- a/core/hashfuncs.h
+++ b/core/hashfuncs.h
@@ -33,6 +33,8 @@
#include "math_defs.h"
#include "math_funcs.h"
+#include "node_path.h"
+#include "string_db.h"
#include "typedefs.h"
#include "ustring.h"
@@ -131,6 +133,7 @@ static inline uint64_t make_uint64_t(T p_in) {
}
struct HashMapHasherDefault {
+
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
@@ -145,6 +148,10 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; }
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; }
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; }
+
+ static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
+ static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
+
//static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); }
};
diff --git a/core/image.cpp b/core/image.cpp
index c08b1ac39b..19440d1718 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -33,6 +33,7 @@
#include "core/io/image_loader.h"
#include "core/os/copymem.h"
#include "hash_map.h"
+#include "math_funcs.h"
#include "print_string.h"
#include "thirdparty/misc/hq2x.h"
@@ -525,7 +526,7 @@ static double _bicubic_interp_kernel(double x) {
}
template <int CC>
-static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
// get source image size
int width = p_src_width;
@@ -555,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
// initial pixel value
- uint8_t *dst = p_dst + (y * p_dst_width + x) * CC;
+ uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC;
double color[CC];
for (int i = 0; i < CC; i++) {
@@ -583,7 +584,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
ox2 = xmax;
// get pixel of original image
- const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC;
+ const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC;
for (int i = 0; i < CC; i++) {
@@ -600,7 +601,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
}
template <int CC>
-static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
enum {
FRAC_BITS = 8,
@@ -655,7 +656,7 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src
}
template <int CC>
-static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
for (uint32_t i = 0; i < p_dst_height; i++) {
@@ -676,6 +677,16 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_
}
}
+static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) {
+
+ uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256);
+
+ for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) {
+
+ p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8;
+ }
+}
+
void Image::resize_to_po2(bool p_square) {
if (!_can_modify(format)) {
@@ -707,6 +718,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
ERR_FAIL();
}
+ bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */;
+
ERR_FAIL_COND(p_width <= 0);
ERR_FAIL_COND(p_height <= 0);
ERR_FAIL_COND(p_width > MAX_WIDTH);
@@ -717,6 +730,32 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
Image dst(p_width, p_height, 0, format);
+ // Setup mipmap-aware scaling
+ Image dst2;
+ int mip1;
+ int mip2;
+ float mip1_weight;
+ if (mipmap_aware) {
+ float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f;
+ if (avg_scale >= 1.0f) {
+ mipmap_aware = false;
+ } else {
+ float level = Math::log(1.0f / avg_scale) / Math::log(2.0f);
+ mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count());
+ mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count());
+ mip1_weight = 1.0f - (level - mip1);
+ }
+ }
+ bool interpolate_mipmaps = mipmap_aware && mip1 != mip2;
+ if (interpolate_mipmaps) {
+ dst2.create(p_width, p_height, 0, format);
+ }
+ bool had_mipmaps = mipmaps;
+ if (interpolate_mipmaps && !had_mipmaps) {
+ generate_mipmaps();
+ }
+ // --
+
PoolVector<uint8_t>::Read r = data.read();
const unsigned char *r_ptr = r.ptr();
@@ -734,13 +773,57 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break;
}
} break;
- case INTERPOLATE_BILINEAR: {
+ case INTERPOLATE_BILINEAR:
+ case INTERPOLATE_TRILINEAR: {
+
+ for (int i = 0; i < 2; ++i) {
+ int src_width;
+ int src_height;
+ const unsigned char *src_ptr;
+
+ if (!mipmap_aware) {
+ if (i == 0) {
+ // Standard behavior
+ src_width = width;
+ src_height = height;
+ src_ptr = r_ptr;
+ } else {
+ // No need for a second iteration
+ break;
+ }
+ } else {
+ if (i == 0) {
+ // Read from the first mipmap that will be interpolated
+ // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level)
+ int offs;
+ _get_mipmap_offset_and_size(mip1, offs, src_width, src_height);
+ src_ptr = r_ptr + offs;
+ } else if (!interpolate_mipmaps) {
+ // No need generate a second image
+ break;
+ } else {
+ // Switch to read from the second mipmap that will be interpolated
+ int offs;
+ _get_mipmap_offset_and_size(mip2, offs, src_width, src_height);
+ src_ptr = r_ptr + offs;
+ // Switch to write to the second destination image
+ w = dst2.data.write();
+ w_ptr = w.ptr();
+ }
+ }
- switch (get_format_pixel_size(format)) {
- case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break;
+ switch (get_format_pixel_size(format)) {
+ case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ }
+ }
+
+ if (interpolate_mipmaps) {
+ // Switch to read again from the first scaled mipmap to overlay it over the second
+ r = dst.data.read();
+ _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format));
}
} break;
@@ -759,7 +842,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
r = PoolVector<uint8_t>::Read();
w = PoolVector<uint8_t>::Write();
- if (mipmaps > 0)
+ if (interpolate_mipmaps) {
+ dst._copy_internals_from(dst2);
+ }
+
+ if (had_mipmaps)
dst.generate_mipmaps();
_copy_internals_from(dst);
@@ -1076,6 +1163,36 @@ void Image::shrink_x2() {
}
}
+void Image::normalize() {
+
+ bool used_mipmaps = has_mipmaps();
+ if (used_mipmaps) {
+ clear_mipmaps();
+ }
+
+ lock();
+
+ for (int y = 0; y < height; y++) {
+
+ for (int x = 0; x < width; x++) {
+
+ Color c = get_pixel(x, y);
+ Vector3 v(c.r * 2.0 - 1.0, c.g * 2.0 - 1.0, c.b * 2.0 - 1.0);
+ v.normalize();
+ c.r = v.x * 0.5 + 0.5;
+ c.g = v.y * 0.5 + 0.5;
+ c.b = v.z * 0.5 + 0.5;
+ set_pixel(x, y, c);
+ }
+ }
+
+ unlock();
+
+ if (used_mipmaps) {
+ generate_mipmaps(true);
+ }
+}
+
Error Image::generate_mipmaps(bool p_renormalize) {
if (!_can_modify(format)) {
@@ -1868,8 +1985,9 @@ void Image::fill(const Color &c) {
unlock();
}
-Ref<Image> (*Image::_png_mem_loader_func)(const uint8_t *, int) = NULL;
-Ref<Image> (*Image::_jpg_mem_loader_func)(const uint8_t *, int) = NULL;
+ImageMemLoadFunc Image::_png_mem_loader_func = NULL;
+ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL;
+ImageMemLoadFunc Image::_webp_mem_loader_func = NULL;
void (*Image::_image_compress_bc_func)(Image *, Image::CompressSource) = NULL;
void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL;
@@ -2327,6 +2445,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer);
ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data");
@@ -2372,6 +2491,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST);
BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR);
BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC);
+ BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR);
BIND_ENUM_CONSTANT(ALPHA_NONE);
BIND_ENUM_CONSTANT(ALPHA_BIT);
@@ -2619,32 +2739,26 @@ String Image::get_format_name(Format p_format) {
}
Error Image::load_png_from_buffer(const PoolVector<uint8_t> &p_array) {
-
- int buffer_size = p_array.size();
-
- ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!_png_mem_loader_func, ERR_INVALID_PARAMETER);
-
- PoolVector<uint8_t>::Read r = p_array.read();
-
- Ref<Image> image = _png_mem_loader_func(r.ptr(), buffer_size);
- ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR);
-
- copy_internals_from(image);
-
- return OK;
+ return _load_from_buffer(p_array, _png_mem_loader_func);
}
Error Image::load_jpg_from_buffer(const PoolVector<uint8_t> &p_array) {
+ return _load_from_buffer(p_array, _jpg_mem_loader_func);
+}
+
+Error Image::load_webp_from_buffer(const PoolVector<uint8_t> &p_array) {
+ return _load_from_buffer(p_array, _webp_mem_loader_func);
+}
+Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader) {
int buffer_size = p_array.size();
ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!_jpg_mem_loader_func, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER);
PoolVector<uint8_t>::Read r = p_array.read();
- Ref<Image> image = _jpg_mem_loader_func(r.ptr(), buffer_size);
+ Ref<Image> image = p_loader(r.ptr(), buffer_size);
ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR);
copy_internals_from(image);
diff --git a/core/image.h b/core/image.h
index e38fa19ded..8c4854e053 100644
--- a/core/image.h
+++ b/core/image.h
@@ -47,6 +47,7 @@
class Image;
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
+typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
class Image : public Resource {
GDCLASS(Image, Resource);
@@ -107,6 +108,8 @@ public:
INTERPOLATE_NEAREST,
INTERPOLATE_BILINEAR,
INTERPOLATE_CUBIC,
+ INTERPOLATE_TRILINEAR,
+ /* INTERPOLATE_TRICUBIC, */
/* INTERPOLATE GAUSS */
};
@@ -118,8 +121,9 @@ public:
//some functions provided by something else
- static Ref<Image> (*_png_mem_loader_func)(const uint8_t *p_png, int p_size);
- static Ref<Image> (*_jpg_mem_loader_func)(const uint8_t *p_png, int p_size);
+ static ImageMemLoadFunc _png_mem_loader_func;
+ static ImageMemLoadFunc _jpg_mem_loader_func;
+ static ImageMemLoadFunc _webp_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, CompressSource p_source);
static void (*_image_compress_pvrtc2_func)(Image *);
@@ -175,6 +179,8 @@ private:
void _set_data(const Dictionary &p_data);
Dictionary _get_data() const;
+ Error _load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader);
+
public:
int get_width() const; ///< Get image width
int get_height() const; ///< Get image height
@@ -220,6 +226,7 @@ public:
Error generate_mipmaps(bool p_renormalize = false);
void clear_mipmaps();
+ void normalize(); //for normal maps
/**
* Create a new image of a given size and format. Current image will be lost
@@ -301,6 +308,7 @@ public:
Error load_png_from_buffer(const PoolVector<uint8_t> &p_array);
Error load_jpg_from_buffer(const PoolVector<uint8_t> &p_array);
+ Error load_webp_from_buffer(const PoolVector<uint8_t> &p_array);
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
Image(const char **p_xpm);
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 8d85e78226..f1620f1493 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -30,6 +30,7 @@
#include "http_client.h"
#include "io/stream_peer_ssl.h"
+#include "version.h"
const char *HTTPClient::_methods[METHOD_MAX] = {
"GET",
@@ -121,16 +122,30 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
}
bool add_clen = p_body.size() > 0;
+ bool add_uagent = true;
+ bool add_accept = true;
for (int i = 0; i < p_headers.size(); i++) {
request += p_headers[i] + "\r\n";
- if (add_clen && p_headers[i].find("Content-Length:") == 0) {
+ if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
add_clen = false;
}
+ if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
+ add_uagent = false;
+ }
+ if (add_accept && p_headers[i].findn("Accept:") == 0) {
+ add_accept = false;
+ }
}
if (add_clen) {
request += "Content-Length: " + itos(p_body.size()) + "\r\n";
// Should it add utf8 encoding?
}
+ if (add_uagent) {
+ request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
+ }
+ if (add_accept) {
+ request += "Accept: */*\r\n";
+ }
request += "\r\n";
CharString cs = request.utf8();
@@ -173,17 +188,31 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
} else {
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
}
+ bool add_uagent = true;
+ bool add_accept = true;
bool add_clen = p_body.length() > 0;
for (int i = 0; i < p_headers.size(); i++) {
request += p_headers[i] + "\r\n";
- if (add_clen && p_headers[i].find("Content-Length:") == 0) {
+ if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
add_clen = false;
}
+ if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
+ add_uagent = false;
+ }
+ if (add_accept && p_headers[i].findn("Accept:") == 0) {
+ add_accept = false;
+ }
}
if (add_clen) {
request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n";
// Should it add utf8 encoding?
}
+ if (add_uagent) {
+ request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
+ }
+ if (add_accept) {
+ request += "Accept: */*\r\n";
+ }
request += "\r\n";
request += p_body;
@@ -250,6 +279,7 @@ void HTTPClient::close() {
chunk_left = 0;
read_until_eof = false;
response_num = 0;
+ handshaking = false;
}
Error HTTPClient::poll() {
@@ -298,16 +328,40 @@ Error HTTPClient::poll() {
} break;
case StreamPeerTCP::STATUS_CONNECTED: {
if (ssl) {
- Ref<StreamPeerSSL> ssl = StreamPeerSSL::create();
- Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
- if (err != OK) {
+ Ref<StreamPeerSSL> ssl;
+ if (!handshaking) {
+ // Connect the StreamPeerSSL and start handshaking
+ ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
+ ssl->set_blocking_handshake_enabled(false);
+ Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
+ if (err != OK) {
+ close();
+ status = STATUS_SSL_HANDSHAKE_ERROR;
+ return ERR_CANT_CONNECT;
+ }
+ connection = ssl;
+ handshaking = true;
+ } else {
+ // We are already handshaking, which means we can use your already active SSL connection
+ ssl = static_cast<Ref<StreamPeerSSL> >(connection);
+ ssl->poll(); // Try to finish the handshake
+ }
+
+ if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
+ // Handshake has been successfull
+ handshaking = false;
+ status = STATUS_CONNECTED;
+ return OK;
+ } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
+ // Handshake has failed
close();
status = STATUS_SSL_HANDSHAKE_ERROR;
return ERR_CANT_CONNECT;
}
- connection = ssl;
+ // ... we will need to poll more for handshake to finish
+ } else {
+ status = STATUS_CONNECTED;
}
- status = STATUS_CONNECTED;
return OK;
} break;
case StreamPeerTCP::STATUS_ERROR:
@@ -640,6 +694,7 @@ HTTPClient::HTTPClient() {
response_num = 0;
ssl = false;
blocking = false;
+ handshaking = false;
read_chunk_size = 4096;
}
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 38ec82ce8c..82b56b01db 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -165,6 +165,7 @@ private:
bool ssl;
bool ssl_verify_host;
bool blocking;
+ bool handshaking;
Vector<uint8_t> response_str;
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index 846c89510e..ffd3ecaed0 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -704,7 +704,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
_send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
}
-Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to) {
+Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) {
ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED);
@@ -714,7 +714,10 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to) {
PoolVector<uint8_t>::Read r = p_data.read();
packet_cache[0] = NETWORK_COMMAND_RAW;
memcpy(&packet_cache[1], &r[0], p_data.size());
+
network_peer->set_target_peer(p_to);
+ network_peer->set_transfer_mode(p_mode);
+
return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
}
@@ -770,7 +773,7 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const {
void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
- ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST));
+ ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index ef56c4c7f2..e47b1830e8 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -104,7 +104,7 @@ public:
void set_root_node(Node *p_node);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
- Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST);
+ Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
// Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp
index 012ba78c6d..c71af6b641 100644
--- a/core/io/stream_peer_ssl.cpp
+++ b/core/io/stream_peer_ssl.cpp
@@ -52,6 +52,14 @@ bool StreamPeerSSL::is_available() {
return available;
}
+void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) {
+ blocking_handshake = p_enabled;
+}
+
+bool StreamPeerSSL::is_blocking_handshake_enabled() const {
+ return blocking_handshake;
+}
+
PoolByteArray StreamPeerSSL::get_project_cert_array() {
PoolByteArray out;
@@ -84,16 +92,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() {
void StreamPeerSSL::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
- ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream);
+ ClassDB::bind_method(D_METHOD("accept_stream"), &StreamPeerSSL::accept_stream);
ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status);
ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream);
+ ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled);
+ ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled");
BIND_ENUM_CONSTANT(STATUS_DISCONNECTED);
BIND_ENUM_CONSTANT(STATUS_CONNECTED);
- BIND_ENUM_CONSTANT(STATUS_ERROR_NO_CERTIFICATE);
+ BIND_ENUM_CONSTANT(STATUS_ERROR);
BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH);
}
StreamPeerSSL::StreamPeerSSL() {
+ blocking_handshake = true;
}
diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h
index 77301a7c87..870704e875 100644
--- a/core/io/stream_peer_ssl.h
+++ b/core/io/stream_peer_ssl.h
@@ -49,14 +49,20 @@ protected:
friend class Main;
static bool initialize_certs;
+ bool blocking_handshake;
+
public:
enum Status {
STATUS_DISCONNECTED,
+ STATUS_HANDSHAKING,
STATUS_CONNECTED,
- STATUS_ERROR_NO_CERTIFICATE,
+ STATUS_ERROR,
STATUS_ERROR_HOSTNAME_MISMATCH
};
+ void set_blocking_handshake_enabled(bool p_enabled);
+ bool is_blocking_handshake_enabled() const;
+
virtual void poll() = 0;
virtual Error accept_stream(Ref<StreamPeer> p_base) = 0;
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0;
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index 6908d7831d..021391da83 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -96,11 +96,11 @@ void AStar::remove_point(int p_id) {
Point *p = points[p_id];
- for (int i = 0; i < p->neighbours.size(); i++) {
+ for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
- Segment s(p_id, p->neighbours[i]->id);
+ Segment s(p_id, E->get()->id);
segments.erase(s);
- p->neighbours[i]->neighbours.erase(p);
+ E->get()->neighbours.erase(p);
}
memdelete(p);
@@ -115,10 +115,10 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
Point *a = points[p_id];
Point *b = points[p_with_id];
- a->neighbours.push_back(b);
+ a->neighbours.insert(b);
if (bidirectional)
- b->neighbours.push_back(a);
+ b->neighbours.insert(a);
Segment s(p_id, p_with_id);
if (s.from == p_id) {
@@ -168,8 +168,8 @@ PoolVector<int> AStar::get_point_connections(int p_id) {
Point *p = points[p_id];
- for (int i = 0; i < p->neighbours.size(); i++) {
- point_list.push_back(p->neighbours[i]->id);
+ for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
+ point_list.push_back(E->get()->id);
}
return point_list;
@@ -242,9 +242,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
bool found_route = false;
- for (int i = 0; i < begin_point->neighbours.size(); i++) {
+ for (Set<Point *>::Element *E = begin_point->neighbours.front(); E; E = E->next()) {
- Point *n = begin_point->neighbours[i];
+ Point *n = E->get();
n->prev_point = begin_point;
n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale;
n->last_pass = pass;
@@ -283,12 +283,10 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
}
Point *p = least_cost_point->self();
- // Open the neighbours for search
- int es = p->neighbours.size();
- for (int i = 0; i < es; i++) {
+ for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
- Point *e = p->neighbours[i];
+ Point *e = E->get();
real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance;
diff --git a/core/math/a_star.h b/core/math/a_star.h
index f89e17c7bb..8c1b5f64cb 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -54,7 +54,7 @@ class AStar : public Reference {
real_t weight_scale;
uint64_t last_pass;
- Vector<Point *> neighbours;
+ Set<Point *> neighbours;
// Used for pathfinding
Point *prev_point;
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 20001bb9a6..f0c0268f31 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -182,8 +182,22 @@ public:
static _ALWAYS_INLINE_ float abs(float g) { return absf(g); }
static _ALWAYS_INLINE_ int abs(int g) { return g > 0 ? g : -g; }
- static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); }
- static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); }
+ static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) {
+ double value = Math::fmod(p_x, p_y);
+ if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) {
+ value += p_y;
+ }
+ value += 0.0;
+ return value;
+ }
+ static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) {
+ float value = Math::fmod(p_x, p_y);
+ if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) {
+ value += p_y;
+ }
+ value += 0.0;
+ return value;
+ }
static _ALWAYS_INLINE_ double deg2rad(double p_y) { return p_y * Math_PI / 180.0; }
static _ALWAYS_INLINE_ float deg2rad(float p_y) { return p_y * Math_PI / 180.0; }
diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp
index 202115e2ca..2371f49561 100644
--- a/core/math/matrix3.cpp
+++ b/core/math/matrix3.cpp
@@ -356,8 +356,7 @@ void Basis::rotate(const Quat &p_quat) {
*this = rotated(p_quat);
}
-// TODO: rename this to get_rotation_euler
-Vector3 Basis::get_rotation() const {
+Vector3 Basis::get_rotation_euler() const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
// See the comment in get_scale() for further information.
@@ -371,6 +370,20 @@ Vector3 Basis::get_rotation() const {
return m.get_euler();
}
+Quat Basis::get_rotation_quat() const {
+ // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
+ // and returns the Euler angles corresponding to the rotation part, complementing get_scale().
+ // See the comment in get_scale() for further information.
+ Basis m = orthonormalized();
+ real_t det = m.determinant();
+ if (det < 0) {
+ // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles.
+ m.scale(Vector3(-1, -1, -1));
+ }
+
+ return m.get_quat();
+}
+
void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
@@ -591,10 +604,9 @@ Basis::operator String() const {
}
Quat Basis::get_quat() const {
- //commenting this check because precision issues cause it to fail when it shouldn't
- //#ifdef MATH_CHECKS
- //ERR_FAIL_COND_V(is_rotation() == false, Quat());
- //#endif
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_rotation() == false, Quat());
+#endif
real_t trace = elements[0][0] + elements[1][1] + elements[2][2];
real_t temp[4];
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index 63d4f5d79d..cd1b51baa6 100644
--- a/core/math/matrix3.h
+++ b/core/math/matrix3.h
@@ -84,9 +84,11 @@ public:
void rotate(const Quat &p_quat);
Basis rotated(const Quat &p_quat) const;
- Vector3 get_rotation() const;
+ Vector3 get_rotation_euler() const;
void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
+ Quat get_rotation_quat() const;
+ Vector3 get_rotation() const { return get_rotation_euler(); };
Vector3 rotref_posscale_decomposition(Basis &rotref) const;
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index b938fc3cfd..67c9048a41 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -139,15 +139,15 @@ bool Quat::is_normalized() const {
Quat Quat::inverse() const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(is_normalized() == false, Quat(0, 0, 0, 0));
+ ERR_FAIL_COND_V(is_normalized() == false, Quat());
#endif
return Quat(-x, -y, -z, w);
}
Quat Quat::slerp(const Quat &q, const real_t &t) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(is_normalized() == false, Quat(0, 0, 0, 0));
- ERR_FAIL_COND_V(q.is_normalized() == false, Quat(0, 0, 0, 0));
+ ERR_FAIL_COND_V(is_normalized() == false, Quat());
+ ERR_FAIL_COND_V(q.is_normalized() == false, Quat());
#endif
Quat to1;
real_t omega, cosom, sinom, scale0, scale1;
@@ -192,7 +192,10 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const {
}
Quat Quat::slerpni(const Quat &q, const real_t &t) const {
-
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Quat());
+ ERR_FAIL_COND_V(q.is_normalized() == false, Quat());
+#endif
const Quat &from = *this;
real_t dot = from.dot(q);
@@ -211,7 +214,10 @@ Quat Quat::slerpni(const Quat &q, const real_t &t) const {
}
Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const {
-
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Quat());
+ ERR_FAIL_COND_V(q.is_normalized() == false, Quat());
+#endif
//the only way to do slerp :|
real_t t2 = (1.0 - t) * t * 2;
Quat sp = this->slerp(q, t);
diff --git a/core/math/quat.h b/core/math/quat.h
index 3e1344a913..6dc8d66f60 100644
--- a/core/math/quat.h
+++ b/core/math/quat.h
@@ -84,7 +84,9 @@ public:
}
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
-
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, v);
+#endif
Vector3 u(x, y, z);
Vector3 uv = u.cross(v);
return v + ((uv * w) + u.cross(uv)) * ((real_t)2);
diff --git a/core/math/transform.cpp b/core/math/transform.cpp
index 7cd186ca60..d1e190f4b9 100644
--- a/core/math/transform.cpp
+++ b/core/math/transform.cpp
@@ -120,11 +120,11 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c)
/* not sure if very "efficient" but good enough? */
Vector3 src_scale = basis.get_scale();
- Quat src_rot = basis.orthonormalized();
+ Quat src_rot = basis.get_rotation_quat();
Vector3 src_loc = origin;
Vector3 dst_scale = p_transform.basis.get_scale();
- Quat dst_rot = p_transform.basis;
+ Quat dst_rot = p_transform.basis.get_rotation_quat();
Vector3 dst_loc = p_transform.origin;
Transform dst; //this could be made faster by using a single function in Basis..
diff --git a/core/message_queue.cpp b/core/message_queue.cpp
index 25ee6eafae..3adaad868a 100644
--- a/core/message_queue.cpp
+++ b/core/message_queue.cpp
@@ -342,7 +342,7 @@ MessageQueue::MessageQueue() {
buffer_end = 0;
buffer_max_used = 0;
- buffer_size = GLOBAL_DEF("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
+ buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
buffer_size *= 1024;
buffer = memnew_arr(uint8_t, buffer_size);
}
diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h
index 2007c3def5..677e8e1fb2 100644
--- a/core/method_ptrcall.h
+++ b/core/method_ptrcall.h
@@ -214,6 +214,50 @@ struct PtrToArg<const T *> {
} \
}
+#define MAKE_VECARG_ALT(m_type, m_type_alt) \
+ template <> \
+ struct PtrToArg<Vector<m_type_alt> > { \
+ _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \
+ const PoolVector<m_type> *dvs = reinterpret_cast<const PoolVector<m_type> *>(p_ptr); \
+ Vector<m_type_alt> ret; \
+ int len = dvs->size(); \
+ ret.resize(len); \
+ { \
+ PoolVector<m_type>::Read r = dvs->read(); \
+ for (int i = 0; i < len; i++) { \
+ ret[i] = r[i]; \
+ } \
+ } \
+ return ret; \
+ } \
+ _FORCE_INLINE_ static void encode(Vector<m_type_alt> p_vec, void *p_ptr) { \
+ PoolVector<m_type> *dv = reinterpret_cast<PoolVector<m_type> *>(p_ptr); \
+ int len = p_vec.size(); \
+ dv->resize(len); \
+ { \
+ PoolVector<m_type>::Write w = dv->write(); \
+ for (int i = 0; i < len; i++) { \
+ w[i] = p_vec[i]; \
+ } \
+ } \
+ } \
+ }; \
+ template <> \
+ struct PtrToArg<const Vector<m_type_alt> &> { \
+ _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \
+ const PoolVector<m_type> *dvs = reinterpret_cast<const PoolVector<m_type> *>(p_ptr); \
+ Vector<m_type_alt> ret; \
+ int len = dvs->size(); \
+ ret.resize(len); \
+ { \
+ PoolVector<m_type>::Read r = dvs->read(); \
+ for (int i = 0; i < len; i++) { \
+ ret[i] = r[i]; \
+ } \
+ } \
+ return ret; \
+ } \
+ }
MAKE_VECARG(String);
MAKE_VECARG(uint8_t);
MAKE_VECARG(int);
@@ -221,6 +265,7 @@ MAKE_VECARG(float);
MAKE_VECARG(Vector2);
MAKE_VECARG(Vector3);
MAKE_VECARG(Color);
+MAKE_VECARG_ALT(String, StringName);
//for stuff that gets converted to Array vectors
#define MAKE_VECARR(m_type) \
diff --git a/core/node_path.cpp b/core/node_path.cpp
index 64983fc091..487d5ee8c6 100644
--- a/core/node_path.cpp
+++ b/core/node_path.cpp
@@ -32,10 +32,7 @@
#include "print_string.h"
-uint32_t NodePath::hash() const {
-
- if (!data)
- return 0;
+void NodePath::_update_hash_cache() const {
uint32_t h = data->absolute ? 1 : 0;
int pc = data->path.size();
@@ -49,13 +46,15 @@ uint32_t NodePath::hash() const {
h = h ^ ssn[i].hash();
}
- return h;
+ data->hash_cache_valid = true;
+ data->hash_cache = h;
}
void NodePath::prepend_period() {
if (data->path.size() && data->path[0].operator String() != ".") {
data->path.insert(0, ".");
+ data->hash_cache_valid = false;
}
}
@@ -114,21 +113,33 @@ bool NodePath::operator==(const NodePath &p_path) const {
if (data->absolute != p_path.data->absolute)
return false;
- if (data->path.size() != p_path.data->path.size())
+ int path_size = data->path.size();
+
+ if (path_size != p_path.data->path.size()) {
return false;
+ }
+
+ int subpath_size = data->subpath.size();
- if (data->subpath.size() != p_path.data->subpath.size())
+ if (subpath_size != p_path.data->subpath.size()) {
return false;
+ }
- for (int i = 0; i < data->path.size(); i++) {
+ const StringName *l_path_ptr = data->path.ptr();
+ const StringName *r_path_ptr = p_path.data->path.ptr();
+
+ for (int i = 0; i < path_size; i++) {
- if (data->path[i] != p_path.data->path[i])
+ if (l_path_ptr[i] != r_path_ptr[i])
return false;
}
- for (int i = 0; i < data->subpath.size(); i++) {
+ const StringName *l_subpath_ptr = data->subpath.ptr();
+ const StringName *r_subpath_ptr = p_path.data->subpath.ptr();
+
+ for (int i = 0; i < subpath_size; i++) {
- if (data->subpath[i] != p_path.data->subpath[i])
+ if (l_subpath_ptr[i] != r_subpath_ptr[i])
return false;
}
@@ -286,6 +297,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) {
data->absolute = p_absolute;
data->path = p_path;
data->has_slashes = true;
+ data->hash_cache_valid = false;
}
NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) {
@@ -301,6 +313,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p
data->path = p_path;
data->subpath = p_subpath;
data->has_slashes = true;
+ data->hash_cache_valid = false;
}
void NodePath::simplify() {
@@ -324,6 +337,7 @@ void NodePath::simplify() {
}
}
}
+ data->hash_cache_valid = false;
}
NodePath NodePath::simplified() const {
@@ -396,6 +410,7 @@ NodePath::NodePath(const String &p_path) {
data->absolute = absolute ? true : false;
data->has_slashes = has_slashes;
data->subpath = subpath;
+ data->hash_cache_valid = false;
if (slices == 0)
return;
diff --git a/core/node_path.h b/core/node_path.h
index 288f39721f..71235029af 100644
--- a/core/node_path.h
+++ b/core/node_path.h
@@ -47,11 +47,15 @@ class NodePath {
StringName concatenated_subpath;
bool absolute;
bool has_slashes;
+ mutable bool hash_cache_valid;
+ mutable uint32_t hash_cache;
};
- Data *data;
+ mutable Data *data;
void unref();
+ void _update_hash_cache() const;
+
public:
_FORCE_INLINE_ StringName get_sname() const {
@@ -78,7 +82,14 @@ public:
NodePath get_parent() const;
- uint32_t hash() const;
+ _FORCE_INLINE_ uint32_t hash() const {
+ if (!data)
+ return 0;
+ if (!data->hash_cache_valid) {
+ _update_hash_cache();
+ }
+ return data->hash_cache;
+ }
operator String() const;
bool is_empty() const;
diff --git a/core/object.cpp b/core/object.cpp
index 1d2aeb7ba5..d86c60a3b8 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -601,8 +601,12 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
_get_property_listv(p_list, p_reversed);
- if (!is_class("Script")) // can still be set, but this is for userfriendlyness
+ if (!is_class("Script")) { // can still be set, but this is for userfriendlyness
+#ifdef TOOLS_ENABLED
+ p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+#endif
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO));
+ }
#ifdef TOOLS_ENABLED
if (editor_section_folding.size()) {
p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
diff --git a/core/object.h b/core/object.h
index 7963a43fd6..8dc3426d1d 100644
--- a/core/object.h
+++ b/core/object.h
@@ -31,6 +31,7 @@
#ifndef OBJECT_H
#define OBJECT_H
+#include "hash_map.h"
#include "list.h"
#include "map.h"
#include "os/rw_lock.h"
@@ -85,6 +86,7 @@ enum PropertyHint {
PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
+ PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
@@ -450,7 +452,7 @@ private:
Signal() { lock = 0; }
};
- HashMap<StringName, Signal, StringNameHasher> signal_map;
+ HashMap<StringName, Signal> signal_map;
List<Connection> connections;
#ifdef DEBUG_ENABLED
SafeRefCount _lock_index;
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 4ebb821a2f..ca6446d015 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -509,6 +509,12 @@ String InputEventMouseButton::as_text() const {
case BUTTON_WHEEL_RIGHT:
button_index_string = "BUTTON_WHEEL_RIGHT";
break;
+ case BUTTON_XBUTTON1:
+ button_index_string = "BUTTON_XBUTTON1";
+ break;
+ case BUTTON_XBUTTON2:
+ button_index_string = "BUTTON_XBUTTON2";
+ break;
default:
button_index_string = itos(get_button_index());
break;
@@ -601,6 +607,12 @@ String InputEventMouseMotion::as_text() const {
case BUTTON_MASK_RIGHT:
button_mask_string = "BUTTON_MASK_RIGHT";
break;
+ case BUTTON_MASK_XBUTTON1:
+ button_mask_string = "BUTTON_MASK_XBUTTON1";
+ break;
+ case BUTTON_MASK_XBUTTON2:
+ button_mask_string = "BUTTON_MASK_XBUTTON2";
+ break;
default:
button_mask_string = itos(get_button_mask());
break;
diff --git a/core/os/input_event.h b/core/os/input_event.h
index 037649ed60..bd1a85ce29 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -53,10 +53,13 @@ enum ButtonList {
BUTTON_WHEEL_DOWN = 5,
BUTTON_WHEEL_LEFT = 6,
BUTTON_WHEEL_RIGHT = 7,
+ BUTTON_XBUTTON1 = 8,
+ BUTTON_XBUTTON2 = 9,
BUTTON_MASK_LEFT = (1 << (BUTTON_LEFT - 1)),
BUTTON_MASK_RIGHT = (1 << (BUTTON_RIGHT - 1)),
BUTTON_MASK_MIDDLE = (1 << (BUTTON_MIDDLE - 1)),
-
+ BUTTON_MASK_XBUTTON1 = (1 << (BUTTON_XBUTTON1 - 1)),
+ BUTTON_MASK_XBUTTON2 = (1 << (BUTTON_XBUTTON2 - 1))
};
enum JoystickList {
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 916c86613e..c51801e3e2 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -58,6 +58,7 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
+ BIND_CONSTANT(NOTIFICATION_CRASH);
};
void MainLoop::set_init_script(const Ref<Script> &p_init_script) {
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 546e4e280c..f96e46141e 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -62,6 +62,7 @@ public:
// fixes this issue.
NOTIFICATION_TRANSLATION_CHANGED = 90,
NOTIFICATION_WM_ABOUT = 91,
+ NOTIFICATION_CRASH = 92,
};
virtual void input_event(const Ref<InputEvent> &p_event);
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 5eed10e30c..89866e4044 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -614,6 +614,9 @@ bool OS::has_feature(const String &p_feature) {
if (_check_internal_feature_support(p_feature))
return true;
+ if (ProjectSettings::get_singleton()->has_custom_feature(p_feature))
+ return true;
+
return false;
}
@@ -656,9 +659,23 @@ const char *OS::get_audio_driver_name(int p_driver) const {
return AudioDriverManager::get_driver(p_driver)->get_name();
}
+void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) {
+ restart_on_exit = p_restart;
+ restart_commandline = p_restart_arguments;
+}
+
+bool OS::is_restart_on_exit_set() const {
+ return restart_on_exit;
+}
+
+List<String> OS::get_restart_on_exit_arguments() const {
+ return restart_commandline;
+}
+
OS::OS() {
void *volatile stack_bottom;
+ restart_on_exit = false;
last_error = NULL;
singleton = this;
_keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
diff --git a/core/os/os.h b/core/os/os.h
index adf01a90e7..4e0cb003fb 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -74,6 +74,9 @@ class OS {
CompositeLogger *_logger;
+ bool restart_on_exit;
+ List<String> restart_commandline;
+
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -182,7 +185,7 @@ public:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
-
+ virtual int get_current_video_driver() const = 0;
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
@@ -496,6 +499,11 @@ public:
bool is_layered_allowed() const { return _allow_layered; }
bool is_hidpi_allowed() const { return _allow_hidpi; }
+
+ void set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments);
+ bool is_restart_on_exit_set() const;
+ List<String> get_restart_on_exit_arguments() const;
+
OS();
virtual ~OS();
};
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index ac4a4b7d15..60e8933751 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -105,6 +105,11 @@ void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_v
ERR_FAIL_COND(!props.has(p_name));
props[p_name].initial = p_value;
}
+void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) {
+
+ ERR_FAIL_COND(!props.has(p_name));
+ props[p_name].restart_if_changed = p_restart;
+}
String ProjectSettings::globalize_path(const String &p_path) const {
@@ -137,7 +142,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
else {
if (p_name == CoreStringNames::get_singleton()->_custom_features) {
- Vector<String> custom_feature_array = p_value;
+ Vector<String> custom_feature_array = String(p_value).split(",");
for (int i = 0; i < custom_feature_array.size(); i++) {
custom_features.insert(custom_feature_array[i]);
@@ -225,6 +230,9 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
else
vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE;
+ if (v->restart_if_changed) {
+ vc.flags |= PROPERTY_USAGE_RESTART_IF_CHANGED;
+ }
vclist.insert(vc);
}
@@ -515,7 +523,11 @@ Error ProjectSettings::_load_settings_text(const String p_path) {
}
} else {
// config_version is checked and dropped
- set(section + "/" + assign, value);
+ if (section == String()) {
+ set(assign, value);
+ } else {
+ set(section + "/" + assign, value);
+ }
}
} else if (next_tag.name != String()) {
section = next_tag.name;
@@ -813,7 +825,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
return OK;
}
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) {
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) {
Variant ret;
if (!ProjectSettings::get_singleton()->has_setting(p_var)) {
@@ -823,6 +835,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) {
ProjectSettings::get_singleton()->set_initial_value(p_var, p_default);
ProjectSettings::get_singleton()->set_builtin_order(p_var);
+ ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed);
return ret;
}
@@ -870,6 +883,10 @@ void ProjectSettings::set_custom_property_info(const String &p_prop, const Prope
custom_prop_info[p_prop].name = p_prop;
}
+const Map<StringName, PropertyInfo> &ProjectSettings::get_custom_property_info() const {
+ return custom_prop_info;
+}
+
void ProjectSettings::set_disable_feature_overrides(bool p_disable) {
disable_feature_overrides = p_disable;
@@ -904,6 +921,10 @@ Variant ProjectSettings::get_setting(const String &p_setting) const {
return get(p_setting);
}
+bool ProjectSettings::has_custom_feature(const String &p_feature) const {
+ return custom_features.has(p_feature);
+}
+
void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
@@ -1068,7 +1089,6 @@ ProjectSettings::ProjectSettings() {
custom_prop_info["rendering/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
custom_prop_info["physics/2d/thread_model"] = PropertyInfo(Variant::INT, "physics/2d/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects");
- GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_mode", 2);
GLOBAL_DEF("debug/settings/profiler/max_functions", 16384);
diff --git a/core/project_settings.h b/core/project_settings.h
index b01e7855aa..75ebc5acc8 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -59,18 +59,21 @@ protected:
Variant initial;
bool hide_from_editor;
bool overridden;
+ bool restart_if_changed;
VariantContainer() :
order(0),
persist(false),
hide_from_editor(false),
- overridden(false) {
+ overridden(false),
+ restart_if_changed(false) {
}
VariantContainer(const Variant &p_variant, int p_order, bool p_persist = false) :
order(p_order),
persist(p_persist),
variant(p_variant),
hide_from_editor(false),
- overridden(false) {
+ overridden(false),
+ restart_if_changed(false) {
}
};
@@ -120,6 +123,7 @@ public:
String globalize_path(const String &p_path) const;
void set_initial_value(const String &p_name, const Variant &p_value);
+ void set_restart_if_changed(const String &p_name, bool p_restart);
bool property_can_revert(const String &p_name);
Variant property_get_revert(const String &p_name);
@@ -137,6 +141,7 @@ public:
Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>(), bool p_merge_with_current = true);
Error save();
void set_custom_property_info(const String &p_prop, const PropertyInfo &p_info);
+ const Map<StringName, PropertyInfo> &get_custom_property_info() const;
Vector<String> get_optimizer_presets() const;
@@ -150,13 +155,16 @@ public:
void set_registering_order(bool p_enable);
+ bool has_custom_feature(const String &p_feature) const;
+
ProjectSettings();
~ProjectSettings();
};
//not a macro any longer
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default);
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false);
#define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value)
+#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
#endif
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 2a611ccf6a..9bcc2d4530 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -191,7 +191,7 @@ void register_core_types() {
void register_core_settings() {
//since in register core types, globals may not e present
- GLOBAL_DEF("network/limits/packet_peer_stream/max_buffer_po2", (16));
+ GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16));
}
void register_core_singletons() {
diff --git a/core/resource.cpp b/core/resource.cpp
index 4cca73be5d..87ff4d3c2a 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -225,15 +225,20 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
continue;
- Variant p = get(E->get().name).duplicate(true);
- if (p.get_type() == Variant::OBJECT && p_subresources) {
+ Variant p = get(E->get().name);
+
+ if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) {
+ p = p.duplicate(p_subresources); //does not make a long of sense but should work?
+ } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) {
RES sr = p;
- if (sr.is_valid())
- p = sr->duplicate(true);
- }
+ if (sr.is_valid()) {
+ r->set(E->get().name, sr->duplicate(p_subresources));
+ }
+ } else {
- r->set(E->get().name, p);
+ r->set(E->get().name, p);
+ }
}
return Ref<Resource>(r);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 0473e2cc71..3955f222f9 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -169,6 +169,10 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
ERR_FAIL();
}
+ if (allow_focus_steal_pid) {
+ OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid);
+ }
+
packet_peer_stream->put_var("debug_enter");
packet_peer_stream->put_var(2);
packet_peer_stream->put_var(p_can_continue);
@@ -1070,6 +1074,10 @@ void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p
physics_frame_time = p_physics_frame_time;
}
+void ScriptDebuggerRemote::set_allow_focus_steal_pid(OS::ProcessID p_pid) {
+ allow_focus_steal_pid = p_pid;
+}
+
ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
ScriptDebuggerRemote::ScriptDebuggerRemote() :
@@ -1091,6 +1099,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
n_errors_dropped(0),
last_msec(0),
msec_count(0),
+ allow_focus_steal_pid(0),
locking(false),
poll_every(0),
request_scene_tree(NULL),
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
index cc12d978d6..b68fc4f9c9 100644
--- a/core/script_debugger_remote.h
+++ b/core/script_debugger_remote.h
@@ -34,6 +34,7 @@
#include "io/packet_peer.h"
#include "io/stream_peer_tcp.h"
#include "list.h"
+#include "os/os.h"
#include "script_language.h"
class ScriptDebuggerRemote : public ScriptDebugger {
@@ -98,6 +99,8 @@ class ScriptDebuggerRemote : public ScriptDebugger {
uint64_t last_msec;
uint64_t msec_count;
+ OS::ProcessID allow_focus_steal_pid;
+
bool locking; //hack to avoid a deadloop
static void _print_handler(void *p_this, const String &p_string, bool p_error);
@@ -171,6 +174,8 @@ public:
virtual void profiling_end();
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ void set_allow_focus_steal_pid(OS::ProcessID p_pid);
+
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 1dab58e29e..37ba3cfc62 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "script_language.h"
+#include "project_settings.h"
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
@@ -103,6 +104,20 @@ void ScriptServer::unregister_language(ScriptLanguage *p_language) {
void ScriptServer::init_languages() {
+ { //load global classes
+ global_classes_clear();
+ if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
+ Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes");
+
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary c = script_classes[i];
+ if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base"))
+ continue;
+ add_global_class(c["class"], c["base"], c["language"], c["path"]);
+ }
+ }
+ }
+
for (int i = 0; i < _language_count; i++) {
_languages[i]->init();
}
@@ -113,6 +128,7 @@ void ScriptServer::finish_languages() {
for (int i = 0; i < _language_count; i++) {
_languages[i]->finish();
}
+ global_classes_clear();
}
void ScriptServer::set_reload_scripts_on_save(bool p_enable) {
@@ -139,6 +155,67 @@ void ScriptServer::thread_exit() {
}
}
+HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
+
+void ScriptServer::global_classes_clear() {
+ global_classes.clear();
+}
+
+void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
+ GlobalScriptClass g;
+ g.language = p_language;
+ g.path = p_path;
+ g.base = p_base;
+ global_classes[p_class] = g;
+}
+void ScriptServer::remove_global_class(const StringName &p_class) {
+ global_classes.erase(p_class);
+}
+bool ScriptServer::is_global_class(const StringName &p_class) {
+ return global_classes.has(p_class);
+}
+StringName ScriptServer::get_global_class_language(const StringName &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
+ return global_classes[p_class].language;
+}
+String ScriptServer::get_global_class_path(const String &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), String());
+ return global_classes[p_class].path;
+}
+
+StringName ScriptServer::get_global_class_base(const String &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), String());
+ return global_classes[p_class].base;
+}
+void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
+ const StringName *K = NULL;
+ List<StringName> classes;
+ while ((K = global_classes.next(K))) {
+ classes.push_back(*K);
+ }
+ classes.sort_custom<StringName::AlphCompare>();
+ for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
+ r_global_classes->push_back(E->get());
+ }
+}
+void ScriptServer::save_global_classes() {
+ List<StringName> gc;
+ get_global_class_list(&gc);
+ Array gcarr;
+ for (List<StringName>::Element *E = gc.front(); E; E = E->next()) {
+ Dictionary d;
+ d["class"] = E->get();
+ d["language"] = global_classes[E->get()].language;
+ d["path"] = global_classes[E->get()].path;
+ d["base"] = global_classes[E->get()].base;
+ gcarr.push_back(d);
+ }
+
+ ProjectSettings::get_singleton()->set("_global_script_classes", gcarr);
+ ProjectSettings::get_singleton()->save();
+}
+
+////////////////////
void ScriptInstance::get_property_state(List<Pair<StringName, Variant> > &state) {
List<PropertyInfo> pinfo;
@@ -347,6 +424,20 @@ Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName &p_n
return Variant::NIL;
}
+void PlaceHolderScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
+
+ if (script.is_valid()) {
+ script->get_script_method_list(p_list);
+ }
+}
+bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const {
+
+ if (script.is_valid()) {
+ return script->has_method(p_method);
+ }
+ return false;
+}
+
void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) {
Set<StringName> new_values;
diff --git a/core/script_language.h b/core/script_language.h
index ad66fc5528..4e81b9b626 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -54,6 +54,14 @@ class ScriptServer {
static bool scripting_enabled;
static bool reload_scripts_on_save;
+ struct GlobalScriptClass {
+ StringName language;
+ String path;
+ String base;
+ };
+
+ static HashMap<StringName, GlobalScriptClass> global_classes;
+
public:
static ScriptEditRequestFunction edit_request_func;
@@ -70,6 +78,16 @@ public:
static void thread_enter();
static void thread_exit();
+ static void global_classes_clear();
+ static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path);
+ static void remove_global_class(const StringName &p_class);
+ static bool is_global_class(const StringName &p_class);
+ static StringName get_global_class_language(const StringName &p_class);
+ static String get_global_class_path(const String &p_class);
+ static StringName get_global_class_base(const String &p_class);
+ static void get_global_class_list(List<StringName> *r_global_classes);
+ static void save_global_classes();
+
static void init_languages();
static void finish_languages();
};
@@ -195,7 +213,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {}
virtual bool is_using_templates() { return false; }
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const = 0;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
virtual Script *create_script() const = 0;
virtual bool has_named_classes() const = 0;
@@ -285,7 +303,10 @@ public:
virtual void frame();
- virtual ~ScriptLanguage(){};
+ virtual bool handles_global_class_type(const String &p_type) const { return false; }
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const { return String(); }
+
+ virtual ~ScriptLanguage() {}
};
extern uint8_t script_encryption_key[32];
@@ -304,8 +325,8 @@ public:
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = NULL) const;
- virtual void get_method_list(List<MethodInfo> *p_list) const {}
- virtual bool has_method(const StringName &p_method) const { return false; }
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST) { return Variant(); }
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
diff --git a/core/string_db.h b/core/string_db.h
index 01d1ca4033..965385b136 100644
--- a/core/string_db.h
+++ b/core/string_db.h
@@ -31,7 +31,6 @@
#ifndef STRING_DB_H
#define STRING_DB_H
-#include "hash_map.h"
#include "os/mutex.h"
#include "safe_refcount.h"
#include "ustring.h"
@@ -168,11 +167,6 @@ public:
~StringName();
};
-struct StringNameHasher {
-
- static _FORCE_INLINE_ uint32_t hash(const StringName &p_string) { return p_string.hash(); }
-};
-
StringName _scs_create(const char *p_chr);
#endif
diff --git a/core/type_info.h b/core/type_info.h
index c1af4fac69..bf497f1e5f 100644
--- a/core/type_info.h
+++ b/core/type_info.h
@@ -194,6 +194,7 @@ MAKE_TEMPLATE_TYPE_INFO(Vector, Color, Variant::POOL_COLOR_ARRAY)
MAKE_TEMPLATE_TYPE_INFO(Vector, Variant, Variant::ARRAY)
MAKE_TEMPLATE_TYPE_INFO(Vector, RID, Variant::ARRAY)
MAKE_TEMPLATE_TYPE_INFO(Vector, Plane, Variant::ARRAY)
+MAKE_TEMPLATE_TYPE_INFO(Vector, StringName, Variant::POOL_STRING_ARRAY)
MAKE_TEMPLATE_TYPE_INFO(PoolVector, Plane, Variant::ARRAY)
MAKE_TEMPLATE_TYPE_INFO(PoolVector, Face3, Variant::POOL_VECTOR3_ARRAY)
diff --git a/core/typedefs.h b/core/typedefs.h
index 4758a5408d..71771ea4e6 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -74,7 +74,7 @@ T *_nullptr() {
#define OFFSET_OF(st, m) \
((size_t)((char *)&(_nullptr<st>()->m) - (char *)0))
- /**
+/**
* Some platforms (devices) not define NULL
*/
@@ -82,7 +82,7 @@ T *_nullptr() {
#define NULL 0
#endif
- /**
+/**
* Windows defines a lot of badly stuff we'll never ever use. undefine it.
*/
@@ -97,6 +97,7 @@ T *_nullptr() {
#undef CLAMP // override standard definition
#undef Error
#undef OK
+#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum
#endif
#include "int_types.h"
@@ -104,7 +105,7 @@ T *_nullptr() {
#include "error_list.h"
#include "error_macros.h"
- /** Generic ABS function, for math uses please use Math::abs */
+/** Generic ABS function, for math uses please use Math::abs */
#ifndef ABS
#define ABS(m_v) ((m_v < 0) ? (-(m_v)) : (m_v))
diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp
index b3f9dd818d..b9a2fdd0ac 100644
--- a/core/undo_redo.cpp
+++ b/core/undo_redo.cpp
@@ -299,26 +299,30 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
}
}
-void UndoRedo::redo() {
+bool UndoRedo::redo() {
- ERR_FAIL_COND(action_level > 0);
+ ERR_FAIL_COND_V(action_level > 0, false);
if ((current_action + 1) >= actions.size())
- return; //nothing to redo
+ return false; //nothing to redo
current_action++;
_process_operation_list(actions[current_action].do_ops.front());
version++;
+
+ return true;
}
-void UndoRedo::undo() {
+bool UndoRedo::undo() {
- ERR_FAIL_COND(action_level > 0);
+ ERR_FAIL_COND_V(action_level > 0, false);
if (current_action < 0)
- return; //nothing to redo
+ return false; //nothing to redo
_process_operation_list(actions[current_action].undo_ops.front());
current_action--;
version--;
+
+ return true;
}
void UndoRedo::clear_history() {
diff --git a/core/undo_redo.h b/core/undo_redo.h
index a373296b73..3a17c78851 100644
--- a/core/undo_redo.h
+++ b/core/undo_redo.h
@@ -109,8 +109,8 @@ public:
void commit_action();
- void redo();
- void undo();
+ bool redo();
+ bool undo();
String get_current_action_name() const;
void clear_history();
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 51f05468e2..bb08dd13c4 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -757,36 +757,32 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int
Vector<String> ret;
const int len = length();
- int from = len;
+ int remaining_len = len;
while (true) {
- int end = rfind(p_splitter, from);
- if (end < 0)
- end = 0;
-
- if (p_allow_empty || (end < from)) {
- const String str = substr(end > 0 ? end + p_splitter.length() : end, end > 0 ? from - end : from + 2);
-
- if (p_maxsplit <= 0) {
- ret.push_back(str);
- } else if (p_maxsplit > 0) {
-
- // Put rest of the string and leave cycle.
- if (p_maxsplit == ret.size()) {
- ret.push_back(substr(0, from + 2));
- break;
- }
-
- // Otherwise, push items until positive limit is reached.
- ret.push_back(str);
+ if (remaining_len < p_splitter.length() || (p_maxsplit > 0 && p_maxsplit == ret.size())) {
+ // no room for another splitter or hit max splits, push what's left and we're done
+ if (p_allow_empty || remaining_len > 0) {
+ ret.push_back(substr(0, remaining_len));
}
+ break;
}
- if (end == 0)
+ int left_edge = rfind(p_splitter, remaining_len - p_splitter.length());
+
+ if (left_edge < 0) {
+ // no more splitters, we're done
+ ret.push_back(substr(0, remaining_len));
break;
+ }
+
+ int substr_start = left_edge + p_splitter.length();
+ if (p_allow_empty || substr_start < remaining_len) {
+ ret.push_back(substr(substr_start, remaining_len - substr_start));
+ }
- from = end - p_splitter.length();
+ remaining_len = left_edge;
}
ret.invert();
@@ -925,8 +921,8 @@ String String::to_upper() const {
for (int i = 0; i < upper.size(); i++) {
- const char s = upper[i];
- const char t = _find_upper(s);
+ const CharType s = upper[i];
+ const CharType t = _find_upper(s);
if (s != t) // avoid copy on write
upper[i] = t;
}
@@ -940,8 +936,8 @@ String String::to_lower() const {
for (int i = 0; i < lower.size(); i++) {
- const char s = lower[i];
- const char t = _find_lower(s);
+ const CharType s = lower[i];
+ const CharType t = _find_lower(s);
if (s != t) // avoid copy on write
lower[i] = t;
}
diff --git a/core/variant.cpp b/core/variant.cpp
index a6df95e310..c48aa57652 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -2012,6 +2012,19 @@ Variant::operator Vector<String>() const {
}
return to;
}
+Variant::operator Vector<StringName>() const {
+
+ PoolVector<String> from = operator PoolVector<String>();
+ Vector<StringName> to;
+ int len = from.size();
+ to.resize(len);
+ for (int i = 0; i < len; i++) {
+
+ to[i] = from[i];
+ }
+ return to;
+}
+
Variant::operator Vector<Vector3>() const {
PoolVector<Vector3> from = operator PoolVector<Vector3>();
@@ -2444,6 +2457,17 @@ Variant::Variant(const Vector<String> &p_array) {
*this = v;
}
+Variant::Variant(const Vector<StringName> &p_array) {
+
+ type = NIL;
+ PoolVector<String> v;
+ int len = p_array.size();
+ v.resize(len);
+ for (int i = 0; i < len; i++)
+ v.set(i, p_array[i]);
+ *this = v;
+}
+
Variant::Variant(const Vector<Vector3> &p_array) {
type = NIL;
diff --git a/core/variant.h b/core/variant.h
index f227e4bfdb..4b245d25e6 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -216,6 +216,7 @@ public:
operator Vector<int>() const;
operator Vector<real_t>() const;
operator Vector<String>() const;
+ operator Vector<StringName>() const;
operator Vector<Vector3>() const;
operator Vector<Color>() const;
operator Vector<RID>() const;
@@ -280,6 +281,7 @@ public:
Variant(const Vector<int> &p_int_array);
Variant(const Vector<real_t> &p_real_array);
Variant(const Vector<String> &p_string_array);
+ Variant(const Vector<StringName> &p_string_array);
Variant(const Vector<Vector3> &p_vector3_array);
Variant(const Vector<Color> &p_color_array);
Variant(const Vector<Plane> &p_array); // helper
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 621af2dfb7..bfa69b1fde 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -3417,8 +3417,17 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
Variant Variant::duplicate(bool deep) const {
switch (type) {
- // case OBJECT:
- // return operator Object *()->duplicate();
+ case OBJECT: {
+ /* breaks stuff :(
+ if (deep && !_get_obj().ref.is_null()) {
+ Ref<Resource> resource = _get_obj().ref;
+ if (resource.is_valid()) {
+ return resource->duplicate(true);
+ }
+ }
+ */
+ return *this;
+ } break;
case DICTIONARY:
return operator Dictionary().duplicate(deep);
case ARRAY:
diff --git a/core/vector.h b/core/vector.h
index f586471e27..c026448ddd 100644
--- a/core/vector.h
+++ b/core/vector.h
@@ -152,6 +152,8 @@ public:
Error insert(int p_pos, const T &p_val);
+ void append_array(const Vector<T> &p_other);
+
template <class C>
void sort_custom() {
@@ -408,6 +410,17 @@ Error Vector<T>::insert(int p_pos, const T &p_val) {
}
template <class T>
+void Vector<T>::append_array(const Vector<T> &p_other) {
+ const int ds = p_other.size();
+ if (ds == 0)
+ return;
+ const int bs = size();
+ resize(bs + ds);
+ for (int i = 0; i < ds; ++i)
+ operator[](bs + i) = p_other[i];
+}
+
+template <class T>
Vector<T>::Vector(const Vector &p_from) {
_ptr = NULL;
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index a4ca830d4d..b5f5fed3f9 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -1145,8 +1145,9 @@
<argument index="1" name="signal" type="String" default="&quot;&quot;">
</argument>
<description>
- Stops the function execution and returns the current state. Call [method GDScriptFunctionState.resume] on the state to resume execution. This invalidates the state.
- Returns anything that was passed to the resume function call. If passed an object and a signal, the execution is resumed when the object's signal is emitted.
+ Stops the function execution and returns the current suspended state to the calling function.
+ From the caller, call [method GDScriptFunctionState.resume] on the state to resume execution. This invalidates the state. Within the resumed function, [code]yield()[/code] returns whatever was passed to the [code]resume()[/code] function call.
+ If passed an object and a signal, the execution is resumed when the object emits the given signal. In this case, [code]yield()[/code] returns the argument passed to [code]emit_signal()[/code] if the signal takes only one argument, or an array containing all the arguments passed to [code]emit_signal()[/code] if the signal takes multiple arguments.
</description>
</method>
</methods>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 35c120cd6a..7fcb827252 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -4,7 +4,16 @@
Generic array datatype.
</brief_description>
<description>
- Generic array, contains several elements of any type, accessible by numerical index starting at 0. Negative indices can be used to count from the right, like in Python. Arrays are always passed by reference.
+ Generic array, contains several elements of any type, accessible by a numerical index starting at 0. Negative indices can be used to count from the back, like in Python (-1 is the last element, -2 the second to last, etc.). Example:
+ [codeblock]
+ var array = ["One", 2, 3, "Four"]
+ print(array[0]) # One
+ print(array[2]) # 3
+ print(array[-1]) # Four
+ array[2] = "Three"
+ print(array[-2]) # Three
+ [/codeblock]
+ Arrays are always passed by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index 2214264dca..1bd902c20e 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -31,6 +31,9 @@
<member name="raw_mode" type="bool" setter="set_raw_mode" getter="is_raw_mode">
If [code]true[/code], allows the color R, G, B component values to go beyond 1.0, which can be used for certain special operations that require it (like tinting without darkening or rendering sprites in HDR).
</member>
+ <member name="deferred_mode" type="bool" setter="set_deferred_mode" getter="is_deferred_mode">
+ If [code]true[/code], the color will apply only after user releases mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues).
+ </member>
</members>
<signals>
<signal name="color_changed">
diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml
index a42d0f196e..ec0381bda5 100644
--- a/doc/classes/ConfigFile.xml
+++ b/doc/classes/ConfigFile.xml
@@ -117,7 +117,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
- Assigns a value to the specified key of the the specified section. If the section and/or the key do not exist, they are created. Passing a [code]null[/code] value deletes the specified key if it exists, and deletes the section if it ends up empty once the key has been removed.
+ Assigns a value to the specified key of the specified section. If the section and/or the key do not exist, they are created. Passing a [code]null[/code] value deletes the specified key if it exists, and deletes the section if it ends up empty once the key has been removed.
</description>
</method>
</methods>
diff --git a/doc/classes/CylinderShape.xml b/doc/classes/CylinderShape.xml
new file mode 100644
index 0000000000..a63cc8831e
--- /dev/null
+++ b/doc/classes/CylinderShape.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CylinderShape" inherits="Shape" category="Core" version="3.1">
+ <brief_description>
+ Cylinder shape for collisions.
+ </brief_description>
+ <description>
+ Cylinder shape for collisions.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="height" type="float" setter="set_height" getter="get_height">
+ The cylinder's height.
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius">
+ The cylinder's radius.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index b9945f3f73..f5fbf8e313 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -235,6 +235,13 @@
<description>
</description>
</method>
+ <method name="get_script_create_dialog">
+ <return type="ScriptCreateDialog">
+ </return>
+ <description>
+ Gets the Editor's dialogue used for making scripts. Note that users can configure it before use.
+ </description>
+ </method>
<method name="get_state" qualifiers="virtual">
<return type="Dictionary">
</return>
diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml
index 664ea33dd6..f95c26c2b0 100644
--- a/doc/classes/EditorScenePostImport.xml
+++ b/doc/classes/EditorScenePostImport.xml
@@ -1,20 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorScenePostImport" inherits="Reference" category="Core" version="3.1">
<brief_description>
+ Post process scenes after import
</brief_description>
<description>
+ The imported scene can be automatically modified right after import by specifying a 'custom script' that inherits from this class. The [method post_import]-method receives the imported scene's root-node and returns the modified version of the scene
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/learning/workflow/assets/importing_scenes.html?highlight=post%20import
</tutorials>
<demos>
+ [codeblock]
+tool # needed so it runs in editor
+extends EditorScenePostImport
+
+# This sample changes all node names
+
+# get called right after the scene is imported and gets the root-node
+func post_import(scene):
+ # change all node names to "modified_[oldnodename]"
+ iterate(scene)
+ return scene # remember to return the imported scene
+
+func iterate(node):
+ if node!=null:
+ node.name = "modified_"+node.name
+ for child in node.get_children():
+ iterate(child)
+[/codeblock]
</demos>
<methods>
+ <method name="get_source_file" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the source-file-path which got imported (e.g. [code]res://scene.dae[/code] )
+ </description>
+ </method>
+ <method name="get_source_folder" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the resource-folder the imported scene-file is located in
+ </description>
+ </method>
<method name="post_import" qualifiers="virtual">
- <return type="void">
+ <return type="Object">
</return>
<argument index="0" name="scene" type="Object">
</argument>
<description>
+ Gets called after the scene got imported and has to return the modified version of the scene
</description>
</method>
</methods>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 9fc7672a80..55693bd49c 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -350,6 +350,15 @@
Loads an image from the binary contents of a PNG file.
</description>
</method>
+ <method name="load_webp_from_buffer">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="buffer" type="PoolByteArray">
+ </argument>
+ <description>
+ Loads an image from the binary contents of a WebP file.
+ </description>
+ </method>
<method name="lock">
<return type="void">
</return>
@@ -584,6 +593,12 @@
</constant>
<constant name="INTERPOLATE_CUBIC" value="2" enum="Interpolation">
</constant>
+ <constant name="INTERPOLATE_TRILINEAR" value="3" enum="Interpolation">
+ Performs bilinear separately on the two most suited mipmap levels, then linearly interpolates between them.
+ It's slower than [code]INTERPOLATE_BILINEAR[/code], but produces higher quality results, with much less aliasing artifacts.
+ If the image does not have mipmaps, they will be generated and used internally, but no mipmaps will be generated on the resulting image. (Note that if you intend to scale multiple copies of the original image, it's better to call [code]generate_mipmaps[/code] on it in advance, to avoid wasting processing power in generating them again and again.)
+ On the other hand, if the image already has mipmaps, they will be used, and a new set will be generated for the resulting image.
+ </constant>
<constant name="ALPHA_NONE" value="0" enum="AlphaMode">
</constant>
<constant name="ALPHA_BIT" value="1" enum="AlphaMode">
diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml
index 19be34978d..c1682e71e5 100644
--- a/doc/classes/Line2D.xml
+++ b/doc/classes/Line2D.xml
@@ -117,5 +117,8 @@
<constant name="LINE_TEXTURE_TILE" value="1" enum="LineTextureMode">
Tiles the texture over the line. The texture need to be imported with Repeat Enabled for it to work properly.
</constant>
+ <constant name="LINE_TEXTURE_STRETCH" value="2" enum="LineTextureMode">
+ Stretches the texture across the line. Import the texture with Repeat Disabled for best results.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml
index 6efbfdb519..d45a3adc9c 100644
--- a/doc/classes/PhysicsServer.xml
+++ b/doc/classes/PhysicsServer.xml
@@ -1413,16 +1413,19 @@
<constant name="SHAPE_CAPSULE" value="4" enum="ShapeType">
The [Shape] is a [CapsuleShape].
</constant>
- <constant name="SHAPE_CONVEX_POLYGON" value="5" enum="ShapeType">
+ <constant name="SHAPE_CYLINDER" value="5" enum="ShapeType">
+ The [Shape] is a [CylinderShape].
+ </constant>
+ <constant name="SHAPE_CONVEX_POLYGON" value="6" enum="ShapeType">
The [Shape] is a [ConvexPolygonShape].
</constant>
- <constant name="SHAPE_CONCAVE_POLYGON" value="6" enum="ShapeType">
+ <constant name="SHAPE_CONCAVE_POLYGON" value="7" enum="ShapeType">
The [Shape] is a [ConcavePolygonShape].
</constant>
- <constant name="SHAPE_HEIGHTMAP" value="7" enum="ShapeType">
+ <constant name="SHAPE_HEIGHTMAP" value="8" enum="ShapeType">
The [Shape] is a [HeightMapShape].
</constant>
- <constant name="SHAPE_CUSTOM" value="8" enum="ShapeType">
+ <constant name="SHAPE_CUSTOM" value="9" enum="ShapeType">
This constant is used internally by the engine. Any attempt to create this kind of shape results in an error.
</constant>
<constant name="AREA_PARAM_GRAVITY" value="0" enum="AreaParameter">
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 89d8c43c00..83d1246e2a 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -507,6 +507,9 @@
</member>
<member name="hide_on_state_item_selection" type="bool" setter="set_hide_on_state_item_selection" getter="is_hide_on_state_item_selection">
</member>
+ <member name="submenu_popup_delay" type="real" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay">
+ Sets the delay time for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. Default value: [code]0.3[/code] seconds.
+ </member>
</members>
<signals>
<signal name="id_focused">
diff --git a/doc/classes/PrismMesh.xml b/doc/classes/PrismMesh.xml
index 268395c37a..11815d299b 100644
--- a/doc/classes/PrismMesh.xml
+++ b/doc/classes/PrismMesh.xml
@@ -14,7 +14,7 @@
</methods>
<members>
<member name="left_to_right" type="float" setter="set_left_to_right" getter="get_left_to_right">
- Displacement of of the upper edge along the x-axis. 0.0 positions edge straight above the bottome left edge. Defaults to 0.5 (positioned on the midpoint).
+ Displacement of the upper edge along the x-axis. 0.0 positions edge straight above the bottome left edge. Defaults to 0.5 (positioned on the midpoint).
</member>
<member name="size" type="Vector3" setter="set_size" getter="get_size">
Size of the prism. Defaults to (2.0, 2.0, 2.0).
diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml
new file mode 100644
index 0000000000..f09d282026
--- /dev/null
+++ b/doc/classes/ScriptCreateDialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ScriptCreateDialog" inherits="ConfirmationDialog" category="Core" version="3.1">
+ <brief_description>
+ The Editor's popup dialog for creating new [Script] files.
+ </brief_description>
+ <description>
+ The ScriptCreateDialog creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling a [method popup]() method.
+ [codeblock]
+ func _ready():
+ dialog.config("Node", "res://new_node.gd") # for in-engine types
+ dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # for script types
+ dialog.popup_centered()
+ [/codeblock]
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="config">
+ <return type="void">
+ </return>
+ <argument index="0" name="inherits" type="String">
+ The dialog's "Inherits" field content.
+ </argument>
+ <argument index="1" name="path" type="String">
+ The dialog's "Path" field content.
+ </argument>
+ <description>
+ Prefills required fields to configure the ScriptCreateDialog for use.
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="script_created">
+ <argument index="0" name="script" type="Object">
+ </argument>
+ <description>
+ Emitted when the user clicks the OK button.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml
index 554842b50e..a0ab4fe878 100644
--- a/doc/classes/Slider.xml
+++ b/doc/classes/Slider.xml
@@ -15,6 +15,8 @@
<members>
<member name="editable" type="bool" setter="set_editable" getter="is_editable">
</member>
+ <member name="scrollable" type="bool" setter="set_scrollable" getter="is_scrollable">
+ </member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode">
</member>
<member name="tick_count" type="int" setter="set_ticks" getter="get_ticks">
diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml
index e806547b7d..68373ebc4f 100644
--- a/doc/classes/SpriteFrames.xml
+++ b/doc/classes/SpriteFrames.xml
@@ -17,7 +17,7 @@
<argument index="0" name="anim" type="String">
</argument>
<description>
- Adds a new animation to the the library.
+ Adds a new animation to the library.
</description>
</method>
<method name="add_frame">
@@ -127,6 +127,13 @@
Changes the animation's name to [code]newname[/code].
</description>
</method>
+ <method name="get_animation_names">
+ <return type="PoolStringArray">
+ </return>
+ <description>
+ Returns an array containing the names associated to each animation. Values are placed in alphabetical order.
+ </description>
+ </method>
<method name="set_animation_loop">
<return type="void">
</return>
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index ee9b7383e5..36408663f6 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -132,6 +132,13 @@
<description>
</description>
</method>
+ <method name="get_breakpoints" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Return an array containing the line number of each breakpoint.
+ </description>
+ </method>
<method name="get_keyword_color" qualifiers="const">
<return type="Color">
</return>
@@ -220,6 +227,12 @@
Insert a given text at the cursor position.
</description>
</method>
+ <method name="is_breakpoint_gutter_enabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="is_folded" qualifiers="const">
<return type="bool">
</return>
@@ -265,6 +278,13 @@
Perform redo operation.
</description>
</method>
+ <method name="remove_breakpoints">
+ <return type="void">
+ </return>
+ <description>
+ Removes all the breakpoints (without firing "breakpoint_toggled" signal).
+ </description>
+ </method>
<method name="search" qualifiers="const">
<return type="PoolIntArray">
</return>
@@ -302,6 +322,14 @@
Select all the text.
</description>
</method>
+ <method name="set_breakpoint_gutter_enabled">
+ <return type="void">
+ </return>
+ <argument index="0" name="enable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_line_as_hidden">
<return type="void">
</return>
@@ -344,6 +372,9 @@
</method>
</methods>
<members>
+ <member name="breakpoint_gutter" type="bool" setter="set_breakpoint_gutter_enabled" getter="is_breakpoint_gutter_enabled">
+ If [code]true[/code] the breakpoint gutter is visible.
+ </member>
<member name="caret_blink" type="bool" setter="cursor_set_blink_enabled" getter="cursor_get_blink_enabled">
If [code]true[/code] the caret (visual cursor) blinks.
</member>
diff --git a/doc/classes/Texture.xml b/doc/classes/Texture.xml
index 3d3ceca150..eb7cfac87b 100644
--- a/doc/classes/Texture.xml
+++ b/doc/classes/Texture.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
A texture works by registering an image in the video hardware, which then can be used in 3D models or 2D [Sprite] or GUI [Control].
+ Textures are often created by loading them from a file. See [method @GDScript.load].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 656063771d..3486b721ca 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -153,6 +153,8 @@
Sets the tile index for the cell given by a Vector2.
An index of [code]-1[/code] clears the cell.
Optionally, the tile can also be flipped, transposed, or given autotile coordinates.
+ Note that data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
+ If you need these to be immediately updated, you can call [method update_dirty_quadrants].
</description>
</method>
<method name="set_cellv">
@@ -172,6 +174,8 @@
Sets the tile index for the given cell.
An index of [code]-1[/code] clears the cell.
Optionally, the tile can also be flipped or transposed.
+ Note that data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
+ If you need these to be immediately updated, you can call [method update_dirty_quadrants].
</description>
</method>
<method name="set_collision_layer_bit">
@@ -217,6 +221,13 @@
Calling with invalid (or missing) parameters applies autotiling rules for the entire tilemap.
</description>
</method>
+ <method name="update_dirty_quadrants">
+ <return type="void">
+ </return>
+ <description>
+ Updates the tile map's quadrants, allowing things such as navigation and collision shapes to be immediately used if modified.
+ </description>
+ </method>
<method name="world_to_map" qualifiers="const">
<return type="Vector2">
</return>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index ba62a385af..6ffeddf5c1 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -207,6 +207,7 @@
<return type="Vector2">
</return>
<description>
+ Returns the vector with all components rounded to the nearest integer, with halfway cases rounded away from zero.
</description>
</method>
<method name="slerp">
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index c754b27f1e..62a480166a 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -208,6 +208,7 @@
<return type="Vector3">
</return>
<description>
+ Returns the vector with all components rounded to the nearest integer, with halfway cases rounded away from zero.
</description>
</method>
<method name="slerp">
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 1e17e72532..08005efa9d 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -58,7 +58,10 @@ Error AudioDriverALSA::init_device() {
#define CHECK_FAIL(m_cond) \
if (m_cond) { \
fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \
- snd_pcm_close(pcm_handle); \
+ if (pcm_handle) { \
+ snd_pcm_close(pcm_handle); \
+ pcm_handle = NULL; \
+ } \
ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN); \
}
@@ -142,8 +145,6 @@ Error AudioDriverALSA::init_device() {
samples_in.resize(period_size * channels);
samples_out.resize(period_size * channels);
- snd_pcm_nonblock(pcm_handle, 0);
-
return OK;
}
@@ -152,7 +153,6 @@ Error AudioDriverALSA::init() {
active = false;
thread_exited = false;
exit_thread = false;
- pcm_open = false;
Error err = init_device();
if (err == OK) {
@@ -168,54 +168,50 @@ void AudioDriverALSA::thread_func(void *p_udata) {
AudioDriverALSA *ad = (AudioDriverALSA *)p_udata;
while (!ad->exit_thread) {
+
+ ad->lock();
+ ad->start_counting_ticks();
+
if (!ad->active) {
for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) {
ad->samples_out[i] = 0;
- };
- } else {
- ad->lock();
+ }
+ } else {
ad->audio_server_process(ad->period_size, ad->samples_in.ptrw());
- ad->unlock();
-
for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) {
ad->samples_out[i] = ad->samples_in[i] >> 16;
}
- };
+ }
int todo = ad->period_size;
int total = 0;
- while (todo) {
- if (ad->exit_thread)
- break;
+ while (todo && !ad->exit_thread) {
uint8_t *src = (uint8_t *)ad->samples_out.ptr();
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
- if (wrote < 0) {
- if (ad->exit_thread)
- break;
+ if (wrote > 0) {
+ total += wrote;
+ todo -= wrote;
+ } else if (wrote == -EAGAIN) {
+ ad->stop_counting_ticks();
+ ad->unlock();
- if (wrote == -EAGAIN) {
- //can't write yet (though this is blocking..)
- usleep(1000);
- continue;
- }
+ OS::get_singleton()->delay_usec(1000);
+
+ ad->lock();
+ ad->start_counting_ticks();
+ } else {
wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0);
if (wrote < 0) {
- //absolute fail
- fprintf(stderr, "ALSA failed and can't recover: %s\n", snd_strerror(wrote));
+ ERR_PRINTS("ALSA: Failed and can't recover: " + String(snd_strerror(wrote)));
ad->active = false;
ad->exit_thread = true;
- break;
}
- continue;
- };
-
- total += wrote;
- todo -= wrote;
- };
+ }
+ }
// User selected a new device, finish the current one so we'll init the new device
if (ad->device_name != ad->new_device) {
@@ -232,10 +228,12 @@ void AudioDriverALSA::thread_func(void *p_udata) {
if (err != OK) {
ad->active = false;
ad->exit_thread = true;
- break;
}
}
}
+
+ ad->stop_counting_ticks();
+ ad->unlock();
};
ad->thread_exited = true;
@@ -296,7 +294,9 @@ String AudioDriverALSA::get_device() {
void AudioDriverALSA::set_device(String device) {
+ lock();
new_device = device;
+ unlock();
}
void AudioDriverALSA::lock() {
@@ -315,29 +315,29 @@ void AudioDriverALSA::unlock() {
void AudioDriverALSA::finish_device() {
- if (pcm_open) {
+ if (pcm_handle) {
snd_pcm_close(pcm_handle);
- pcm_open = NULL;
+ pcm_handle = NULL;
}
}
void AudioDriverALSA::finish() {
- if (!thread)
- return;
-
- exit_thread = true;
- Thread::wait_to_finish(thread);
+ if (thread) {
+ exit_thread = true;
+ Thread::wait_to_finish(thread);
- finish_device();
+ memdelete(thread);
+ thread = NULL;
- memdelete(thread);
- if (mutex) {
- memdelete(mutex);
- mutex = NULL;
+ if (mutex) {
+ memdelete(mutex);
+ mutex = NULL;
+ }
}
- thread = NULL;
-};
+
+ finish_device();
+}
AudioDriverALSA::AudioDriverALSA() {
diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h
index 2878e100a2..e2a2325cf3 100644
--- a/drivers/alsa/audio_driver_alsa.h
+++ b/drivers/alsa/audio_driver_alsa.h
@@ -66,7 +66,6 @@ class AudioDriverALSA : public AudioDriver {
bool active;
bool thread_exited;
mutable bool exit_thread;
- bool pcm_open;
public:
const char *get_name() const {
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 6e451eabcd..ac21de91e4 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -52,7 +52,9 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID
}
#endif
-Error AudioDriverCoreAudio::init_device() {
+Error AudioDriverCoreAudio::init() {
+ mutex = Mutex::create();
+
AudioComponentDescription desc;
zeromem(&desc, sizeof(desc));
desc.componentType = kAudioUnitType_Output;
@@ -69,6 +71,16 @@ Error AudioDriverCoreAudio::init_device() {
OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
+#ifdef OSX_ENABLED
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+#endif
+
AudioStreamBasicDescription strdesc;
zeromem(&strdesc, sizeof(strdesc));
@@ -90,7 +102,7 @@ Error AudioDriverCoreAudio::init_device() {
break;
}
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
zeromem(&strdesc, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
@@ -105,7 +117,7 @@ Error AudioDriverCoreAudio::init_device() {
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc));
ERR_FAIL_COND_V(result != noErr, FAILED);
- int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
@@ -135,42 +147,6 @@ Error AudioDriverCoreAudio::init_device() {
return OK;
}
-Error AudioDriverCoreAudio::finish_device() {
- OSStatus result;
-
- if (active) {
- result = AudioOutputUnitStop(audio_unit);
- ERR_FAIL_COND_V(result != noErr, FAILED);
-
- active = false;
- }
-
- result = AudioUnitUninitialize(audio_unit);
- ERR_FAIL_COND_V(result != noErr, FAILED);
-
- return OK;
-}
-
-Error AudioDriverCoreAudio::init() {
- OSStatus result;
-
- mutex = Mutex::create();
- active = false;
- channels = 2;
-
-#ifdef OSX_ENABLED
- AudioObjectPropertyAddress prop;
- prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
- prop.mScope = kAudioObjectPropertyScopeGlobal;
- prop.mElement = kAudioObjectPropertyElementMaster;
-
- result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
- ERR_FAIL_COND_V(result != noErr, FAILED);
-#endif
-
- return init_device();
-};
-
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
@@ -187,6 +163,8 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
return 0;
};
+ ad->start_counting_ticks();
+
for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
AudioBuffer *abuf = &ioData->mBuffers[i];
@@ -208,6 +186,7 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
};
};
+ ad->stop_counting_ticks();
ad->unlock();
return 0;
@@ -370,6 +349,7 @@ void AudioDriverCoreAudio::set_device(String device) {
}
if (!found) {
+ // If we haven't found the desired device get the system default one
UInt32 size = sizeof(AudioDeviceID);
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
@@ -406,7 +386,28 @@ bool AudioDriverCoreAudio::try_lock() {
void AudioDriverCoreAudio::finish() {
OSStatus result;
- finish_device();
+ lock();
+
+ AURenderCallbackStruct callback;
+ zeromem(&callback, sizeof(AURenderCallbackStruct));
+ result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
+ if (result != noErr) {
+ ERR_PRINT("AudioUnitSetProperty failed");
+ }
+
+ if (active) {
+ result = AudioOutputUnitStop(audio_unit);
+ if (result != noErr) {
+ ERR_PRINT("AudioOutputUnitStop failed");
+ }
+
+ active = false;
+ }
+
+ result = AudioUnitUninitialize(audio_unit);
+ if (result != noErr) {
+ ERR_PRINT("AudioUnitUninitialize failed");
+ }
#ifdef OSX_ENABLED
AudioObjectPropertyAddress prop;
@@ -420,13 +421,13 @@ void AudioDriverCoreAudio::finish() {
}
#endif
- AURenderCallbackStruct callback;
- zeromem(&callback, sizeof(AURenderCallbackStruct));
- result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
+ result = AudioComponentInstanceDispose(audio_unit);
if (result != noErr) {
- ERR_PRINT("AudioUnitSetProperty failed");
+ ERR_PRINT("AudioComponentInstanceDispose failed");
}
+ unlock();
+
if (mutex) {
memdelete(mutex);
mutex = NULL;
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index c44e225521..99c910498e 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -68,9 +68,6 @@ class AudioDriverCoreAudio : public AudioDriver {
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
- Error init_device();
- Error finish_device();
-
public:
const char *get_name() const {
return "CoreAudio";
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 312d5aa378..bab89f649a 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -67,7 +67,7 @@ public:
void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {}
void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) {}
- void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {}
+ void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {}
void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {}
@@ -235,6 +235,7 @@ public:
void textures_keep_original(bool p_enable) {}
void texture_set_proxy(RID p_proxy, RID p_base) {}
+ void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {}
/* SKY API */
@@ -408,19 +409,23 @@ public:
virtual RID multimesh_create() { return RID(); }
- void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format) {}
+ void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE) {}
int multimesh_get_instance_count(RID p_multimesh) const { return 0; }
void multimesh_set_mesh(RID p_multimesh, RID p_mesh) {}
void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) {}
void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {}
void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {}
+ void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {}
RID multimesh_get_mesh(RID p_multimesh) const { return RID(); }
Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const { return Transform(); }
Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { return Transform2D(); }
Color multimesh_instance_get_color(RID p_multimesh, int p_index) const { return Color(); }
+ Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { return Color(); }
+
+ void multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) {}
void multimesh_set_visible_instances(RID p_multimesh, int p_visible) {}
int multimesh_get_visible_instances(RID p_multimesh) const { return 0; }
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp
index cc8e3277b9..fb150d6820 100644
--- a/drivers/gles2/rasterizer_canvas_gles2.cpp
+++ b/drivers/gles2/rasterizer_canvas_gles2.cpp
@@ -140,6 +140,10 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con
texture = texture->get_ptr();
+ if (texture->redraw_if_visible) {
+ VisualServerRaster::redraw_request();
+ }
+
if (texture->render_target) {
texture->render_target->used_in_frame = true;
}
@@ -405,8 +409,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
Rect2 dst_rect = Rect2(r->rect.position, r->rect.size);
- state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
-
if (dst_rect.size.width < 0) {
dst_rect.position.x += dst_rect.size.width;
dst_rect.size.width *= -1;
@@ -659,6 +661,8 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
if (state.canvas_shader.bind())
_set_uniforms();
+ _bind_canvas_texture(RID(), RID());
+
if (pline->triangles.size()) {
_draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1);
} else {
@@ -733,6 +737,9 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
int w = current_clip->final_clip_rect.size.x;
int h = current_clip->final_clip_rect.size.y;
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
+
glScissor(x, y, w, h);
reclip = false;
@@ -821,7 +828,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
if (current_clip) {
glEnable(GL_SCISSOR_TEST);
- glScissor(current_clip->final_clip_rect.position.x, (rt_size.height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.height)), current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
+ int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
+ glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
} else {
glDisable(GL_SCISSOR_TEST);
}
@@ -903,6 +913,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
t = t->get_ptr();
+ if (t->redraw_if_visible) {
+ VisualServerRaster::redraw_request();
+ }
+
glBindTexture(t->target, t->tex_id);
}
} else {
@@ -969,7 +983,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
if (reclip) {
glEnable(GL_SCISSOR_TEST);
- glScissor(current_clip->final_clip_rect.position.x, (rt_size.height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.height)), current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
+ int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
+ glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
}
p_item_list = p_item_list->next;
diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp
index ab48e682d6..9ea20ff15a 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -203,7 +203,7 @@ void RasterizerGLES2::initialize() {
#endif // GLAD_ENABLED
- // For debugging
+ // For debugging
#ifdef GLES_OVER_GL
if (GLAD_GL_ARB_debug_output) {
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index bb39cbcbd5..f7712be5d0 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -144,7 +144,7 @@ void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, float p
void RasterizerSceneGLES2::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) {
}
-void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
+void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
}
void RasterizerSceneGLES2::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h
index 99f034afed..110222f709 100644
--- a/drivers/gles2/rasterizer_scene_gles2.h
+++ b/drivers/gles2/rasterizer_scene_gles2.h
@@ -212,7 +212,7 @@ public:
virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture);
virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness);
- virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness);
+ virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness);
virtual void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale);
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index de7359a18b..0dc506d991 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -674,6 +674,14 @@ void RasterizerStorageGLES2::texture_set_proxy(RID p_texture, RID p_proxy) {
}
}
+void RasterizerStorageGLES2::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {
+
+ Texture *texture = texture_owner.getornull(p_texture);
+ ERR_FAIL_COND(!texture);
+
+ texture->redraw_if_visible = p_enable;
+}
+
void RasterizerStorageGLES2::texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) {
// TODO
}
@@ -1182,7 +1190,7 @@ RID RasterizerStorageGLES2::multimesh_create() {
return RID();
}
-void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format) {
+void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data) {
}
int RasterizerStorageGLES2::multimesh_get_instance_count(RID p_multimesh) const {
@@ -1201,6 +1209,9 @@ void RasterizerStorageGLES2::multimesh_instance_set_transform_2d(RID p_multimesh
void RasterizerStorageGLES2::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
}
+void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
+}
+
RID RasterizerStorageGLES2::multimesh_get_mesh(RID p_multimesh) const {
return RID();
}
@@ -1217,6 +1228,13 @@ Color RasterizerStorageGLES2::multimesh_instance_get_color(RID p_multimesh, int
return Color();
}
+Color RasterizerStorageGLES2::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
+ return Color();
+}
+
+void RasterizerStorageGLES2::multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) {
+}
+
void RasterizerStorageGLES2::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
}
@@ -1987,6 +2005,8 @@ void RasterizerStorageGLES2::initialize() {
}
}
+ config.shrink_textures_x2 = false;
+
frame.count = 0;
frame.prev_tick = 0;
frame.delta = 0;
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index b735f2e148..30e13a9f65 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -182,6 +182,8 @@ public:
Ref<Image> images[6];
+ bool redraw_if_visible;
+
Texture() {
flags = 0;
width = 0;
@@ -205,6 +207,8 @@ public:
proxy = NULL;
render_target = NULL;
+
+ redraw_if_visible = false;
}
_ALWAYS_INLINE_ Texture *get_ptr() {
@@ -264,6 +268,8 @@ public:
virtual void texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata);
virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata);
+ virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable);
+
/* SKY API */
virtual RID sky_create();
@@ -508,19 +514,23 @@ public:
virtual RID multimesh_create();
- virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format);
+ virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE);
virtual int multimesh_get_instance_count(RID p_multimesh) const;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh);
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform);
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform);
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color);
+ virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color);
virtual RID multimesh_get_mesh(RID p_multimesh) const;
virtual Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const;
+ virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const;
+
+ virtual void multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array);
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
virtual int multimesh_get_visible_instances(RID p_multimesh) const;
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index aa55e72083..549d91a5a0 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -721,8 +721,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL"] = "normal";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP"] = "normal_map";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP_DEPTH"] = "normal_depth";
- actions[VS::SHADER_CANVAS_ITEM].renames["UV"] = "uv_interp";
- actions[VS::SHADER_CANVAS_ITEM].renames["COLOR"] = "color";
actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE"] = "color_texture";
actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE_PIXEL_SIZE"] = "color_texpixel_size";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL_TEXTURE"] = "normal_texture";
@@ -736,7 +734,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_HEIGHT"] = "light_height";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_COLOR"] = "light_color";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_UV"] = "light_uv";
- //actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_SHADOW_COLOR"]="light_shadow_color";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT"] = "light";
actions[VS::SHADER_CANVAS_ITEM].renames["SHADOW_COLOR"] = "shadow_color";
@@ -768,7 +765,8 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp";
actions[VS::SHADER_SPATIAL].renames["COLOR"] = "color_interp";
actions[VS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize";
- //actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"]=ShaderLanguage::TYPE_INT;
+ // gl_InstanceID is not available in OpenGL ES 2.0
+ actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "0";
//builtins
@@ -790,13 +788,11 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss";
actions[VS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy";
actions[VS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow";
- //actions[VS::SHADER_SPATIAL].renames["SSS_SPREAD"] = "sss_spread";
actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength";
actions[VS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission";
actions[VS::SHADER_SPATIAL].renames["AO"] = "ao";
actions[VS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect";
actions[VS::SHADER_SPATIAL].renames["EMISSION"] = "emission";
- //actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"]=ShaderLanguage::TYPE_VEC2;
actions[VS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord";
actions[VS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom";
actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv";
@@ -838,8 +834,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
actions[VS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
- actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength";
-
actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index bb4c8ab4d7..5e13bed198 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -211,6 +211,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
} else {
+ if (texture->redraw_if_visible) { //check before proxy, because this is usually used with proxies
+ VisualServerRaster::redraw_request();
+ }
+
texture = texture->get_ptr();
if (texture->render_target)
@@ -248,6 +252,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
} else {
+ if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies
+ VisualServerRaster::redraw_request();
+ }
+
normal_map = normal_map->get_ptr();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normal_map->tex_id);
@@ -832,6 +840,9 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
if (!particles)
break;
+ if (particles->inactive && !particles->emitting)
+ break;
+
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
VisualServerRaster::redraw_request();
@@ -997,13 +1008,11 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
glEnable(GL_SCISSOR_TEST);
//glScissor(viewport.x+current_clip->final_clip_rect.pos.x,viewport.y+ (viewport.height-(current_clip->final_clip_rect.pos.y+current_clip->final_clip_rect.size.height)),
//current_clip->final_clip_rect.size.width,current_clip->final_clip_rect.size.height);
-
- int x = current_clip->final_clip_rect.position.x;
int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
- int w = current_clip->final_clip_rect.size.x;
- int h = current_clip->final_clip_rect.size.y;
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
- glScissor(x, y, w, h);
+ glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y);
reclip = false;
}
@@ -1138,7 +1147,11 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
if (current_clip) {
glEnable(GL_SCISSOR_TEST);
- glScissor(current_clip->final_clip_rect.position.x, (rt_size.height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.height)), current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
+ int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
+
+ glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y);
} else {
@@ -1261,6 +1274,10 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
continue;
}
+ if (t->redraw_if_visible) { //check before proxy, because this is usually used with proxies
+ VisualServerRaster::redraw_request();
+ }
+
t = t->get_ptr();
if (storage->config.srgb_decode_supported && t->using_srgb) {
@@ -1515,7 +1532,10 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
if (reclip) {
glEnable(GL_SCISSOR_TEST);
- glScissor(current_clip->final_clip_rect.position.x, (rt_size.height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.height)), current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
+ int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y);
+ if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP])
+ y = current_clip->final_clip_rect.position.y;
+ glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height);
}
p_item_list = p_item_list->next;
@@ -1875,7 +1895,7 @@ void RasterizerCanvasGLES3::initialize() {
}
{
- uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
+ uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
poly_size *= 1024; //kb
poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float));
glGenBuffers(1, &data.polygon_buffer);
@@ -1922,7 +1942,7 @@ void RasterizerCanvasGLES3::initialize() {
glGenVertexArrays(1, &data.polygon_buffer_pointer_array);
- uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128);
+ uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128);
index_size *= 1024; //kb
glGenBuffers(1, &data.polygon_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 03ff84c093..2b7cea8508 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -896,7 +896,7 @@ void RasterizerSceneGLES3::environment_set_ssr(RID p_env, bool p_enable, int p_m
env->ssr_roughness = p_roughness;
}
-void RasterizerSceneGLES3::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
+void RasterizerSceneGLES3::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
@@ -908,6 +908,7 @@ void RasterizerSceneGLES3::environment_set_ssao(RID p_env, bool p_enable, float
env->ssao_intensity2 = p_intensity2;
env->ssao_bias = p_bias;
env->ssao_light_affect = p_light_affect;
+ env->ssao_ao_channel_affect = p_ao_channel_affect;
env->ssao_color = p_color;
env->ssao_filter = p_blur;
env->ssao_quality = p_quality;
@@ -1224,7 +1225,12 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m
} else {
+ if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies
+ VisualServerRaster::redraw_request();
+ }
+
t = t->get_ptr(); //resolve for proxies
+
#ifdef TOOLS_ENABLED
if (t->detect_3d) {
t->detect_3d(t->detect_3d_ud);
@@ -1335,7 +1341,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer
- int stride = (multi_mesh->xform_floats + multi_mesh->color_floats) * 4;
+ int stride = (multi_mesh->xform_floats + multi_mesh->color_floats + multi_mesh->custom_data_floats) * 4;
glEnableVertexAttribArray(8);
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
glVertexAttribDivisor(8, 1);
@@ -1356,6 +1362,8 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
color_ofs = 8 * 4;
}
+ int custom_data_ofs = color_ofs;
+
switch (multi_mesh->color_format) {
case VS::MULTIMESH_COLOR_NONE: {
@@ -1366,12 +1374,33 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
glEnableVertexAttribArray(11);
glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, ((uint8_t *)NULL) + color_ofs);
glVertexAttribDivisor(11, 1);
+ custom_data_ofs += 4;
} break;
case VS::MULTIMESH_COLOR_FLOAT: {
glEnableVertexAttribArray(11);
glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + color_ofs);
glVertexAttribDivisor(11, 1);
+ custom_data_ofs += 4 * 4;
+ } break;
+ }
+
+ switch (multi_mesh->custom_data_format) {
+
+ case VS::MULTIMESH_CUSTOM_DATA_NONE: {
+ glDisableVertexAttribArray(12);
+ glVertexAttrib4f(12, 1, 1, 1, 1);
+ } break;
+ case VS::MULTIMESH_CUSTOM_DATA_8BIT: {
+ glEnableVertexAttribArray(12);
+ glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, ((uint8_t *)NULL) + custom_data_ofs);
+ glVertexAttribDivisor(12, 1);
+
+ } break;
+ case VS::MULTIMESH_CUSTOM_DATA_FLOAT: {
+ glEnableVertexAttribArray(12);
+ glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + custom_data_ofs);
+ glVertexAttribDivisor(12, 1);
} break;
}
@@ -1545,6 +1574,11 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
RasterizerStorageGLES3::Texture *t = storage->texture_owner.get(c.texture);
t = t->get_ptr(); //resolve for proxies
+
+ if (t->redraw_if_visible) {
+ VisualServerRaster::redraw_request();
+ }
+
#ifdef TOOLS_ENABLED
if (t->detect_3d) {
t->detect_3d(t->detect_3d_ud);
@@ -2507,6 +2541,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr
state.env_radiance_data.ambient_contribution = env->ambient_sky_contribution;
state.ubo_data.ambient_occlusion_affect_light = env->ssao_light_affect;
+ state.ubo_data.ambient_occlusion_affect_ssao = env->ssao_ao_channel_affect;
//fog
@@ -4051,6 +4086,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
state.ubo_data.z_slope_scale = 0;
state.ubo_data.shadow_dual_paraboloid_render_side = 0;
state.ubo_data.shadow_dual_paraboloid_render_zfar = 0;
+ state.ubo_data.opaque_prepass_threshold = 0.99;
p_cam_projection.get_viewport_size(state.ubo_data.viewport_size[0], state.ubo_data.viewport_size[1]);
@@ -4663,6 +4699,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_
state.ubo_data.z_slope_scale = normal_bias;
state.ubo_data.shadow_dual_paraboloid_render_side = dp_direction;
state.ubo_data.shadow_dual_paraboloid_render_zfar = zfar;
+ state.ubo_data.opaque_prepass_threshold = 0.1;
_setup_environment(NULL, light_projection, light_transform);
@@ -4831,7 +4868,7 @@ void RasterizerSceneGLES3::initialize() {
glBufferData(GL_UNIFORM_BUFFER, sizeof(State::EnvironmentRadianceUBO), &state.env_radiance_ubo, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
- render_list.max_elements = GLOBAL_DEF("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS);
+ render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS);
if (render_list.max_elements > 1000000)
render_list.max_elements = 1000000;
if (render_list.max_elements < 1024)
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index a6faeef473..cf387a69bc 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -140,6 +140,8 @@ public:
float reflection_multiplier;
float subsurface_scatter_width;
float ambient_occlusion_affect_light;
+ float ambient_occlusion_affect_ssao;
+ float opaque_prepass_threshold;
uint32_t fog_depth_enabled;
float fog_depth_begin;
@@ -151,6 +153,7 @@ public:
float fog_height_max;
float fog_height_curve;
// make sure this struct is padded to be a multiple of 16 bytes for webgl
+ float pad[2];
} ubo_data;
@@ -385,6 +388,7 @@ public:
float ssao_radius2;
float ssao_bias;
float ssao_light_affect;
+ float ssao_ao_channel_affect;
Color ssao_color;
VS::EnvironmentSSAOQuality ssao_quality;
float ssao_bilateral_sharpness;
@@ -465,6 +469,7 @@ public:
ssao_radius2 = 0.0;
ssao_bias = 0.01;
ssao_light_affect = 0;
+ ssao_ao_channel_affect = 0;
ssao_filter = VS::ENV_SSAO_BLUR_3x3;
ssao_quality = VS::ENV_SSAO_QUALITY_LOW;
ssao_bilateral_sharpness = 4;
@@ -543,7 +548,7 @@ public:
virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture);
virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness);
- virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness);
+ virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness);
virtual void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 11ab957458..9e389a353e 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -1328,6 +1328,13 @@ void RasterizerStorageGLES3::texture_set_proxy(RID p_texture, RID p_proxy) {
}
}
+void RasterizerStorageGLES3::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {
+
+ Texture *texture = texture_owner.get(p_texture);
+ ERR_FAIL_COND(!texture);
+ texture->redraw_if_visible = p_enable;
+}
+
RID RasterizerStorageGLES3::sky_create() {
Sky *sky = memnew(Sky);
@@ -3233,7 +3240,7 @@ void RasterizerStorageGLES3::mesh_surface_update_region(RID p_mesh, int p_surfac
PoolVector<uint8_t>::Read r = p_data.read();
- glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->array_id);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id);
glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr());
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
@@ -3397,6 +3404,7 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap
return bsarr;
}
+
Vector<AABB> RasterizerStorageGLES3::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
const Mesh *mesh = mesh_owner.getornull(p_mesh);
@@ -3448,6 +3456,7 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) {
mesh->instance_change_notify();
}
+
int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const {
const Mesh *mesh = mesh_owner.getornull(p_mesh);
@@ -3461,6 +3470,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb
ERR_FAIL_COND(!mesh);
mesh->custom_aabb = p_aabb;
+ mesh->instance_change_notify();
}
AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const {
@@ -3816,12 +3826,12 @@ RID RasterizerStorageGLES3::multimesh_create() {
return multimesh_owner.make_rid(multimesh);
}
-void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format) {
+void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data_format) {
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
ERR_FAIL_COND(!multimesh);
- if (multimesh->size == p_instances && multimesh->transform_format == p_transform_format && multimesh->color_format == p_color_format)
+ if (multimesh->size == p_instances && multimesh->transform_format == p_transform_format && multimesh->color_format == p_color_format && multimesh->custom_data_format == p_data_format)
return;
if (multimesh->buffer) {
@@ -3832,6 +3842,7 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances
multimesh->size = p_instances;
multimesh->transform_format = p_transform_format;
multimesh->color_format = p_color_format;
+ multimesh->custom_data_format = p_data_format;
if (multimesh->size) {
@@ -3849,11 +3860,22 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances
multimesh->color_floats = 4;
}
- int format_floats = multimesh->color_floats + multimesh->xform_floats;
+ if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE) {
+ multimesh->custom_data_floats = 0;
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) {
+ multimesh->custom_data_floats = 1;
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) {
+ multimesh->custom_data_floats = 4;
+ }
+
+ int format_floats = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
+
multimesh->data.resize(format_floats * p_instances);
- for (int i = 0; i < p_instances; i += format_floats) {
+
+ for (int i = 0; i < p_instances * format_floats; i += format_floats) {
int color_from = 0;
+ int custom_data_from = 0;
if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) {
multimesh->data[i + 0] = 1.0;
@@ -3865,6 +3887,7 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances
multimesh->data[i + 6] = 0.0;
multimesh->data[i + 7] = 0.0;
color_from = 8;
+ custom_data_from = 8;
} else {
multimesh->data[i + 0] = 1.0;
multimesh->data[i + 1] = 0.0;
@@ -3879,6 +3902,7 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances
multimesh->data[i + 10] = 1.0;
multimesh->data[i + 11] = 0.0;
color_from = 12;
+ custom_data_from = 12;
}
if (multimesh->color_format == VS::MULTIMESH_COLOR_NONE) {
@@ -3892,12 +3916,33 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances
cu.colu = 0xFFFFFFFF;
multimesh->data[i + color_from + 0] = cu.colf;
+ custom_data_from = color_from + 1;
} else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) {
multimesh->data[i + color_from + 0] = 1.0;
multimesh->data[i + color_from + 1] = 1.0;
multimesh->data[i + color_from + 2] = 1.0;
multimesh->data[i + color_from + 3] = 1.0;
+ custom_data_from = color_from + 4;
+ }
+
+ if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE) {
+ //none
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) {
+
+ union {
+ uint32_t colu;
+ float colf;
+ } cu;
+
+ cu.colu = 0;
+ multimesh->data[i + custom_data_from + 0] = cu.colf;
+
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) {
+ multimesh->data[i + custom_data_from + 0] = 0.0;
+ multimesh->data[i + custom_data_from + 1] = 0.0;
+ multimesh->data[i + custom_data_from + 2] = 0.0;
+ multimesh->data[i + custom_data_from + 3] = 0.0;
}
}
@@ -3958,7 +4003,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform(RID p_multimesh, i
ERR_FAIL_INDEX(p_index, multimesh->size);
ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D);
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index];
dataptr[0] = p_transform.basis.elements[0][0];
@@ -3989,7 +4034,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform_2d(RID p_multimesh
ERR_FAIL_INDEX(p_index, multimesh->size);
ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D);
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index];
dataptr[0] = p_transform.elements[0][0];
@@ -4015,7 +4060,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p
ERR_FAIL_INDEX(p_index, multimesh->size);
ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE);
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats];
if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) {
@@ -4041,6 +4086,38 @@ void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p
}
}
+void RasterizerStorageGLES3::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data) {
+
+ MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+ ERR_FAIL_INDEX(p_index, multimesh->size);
+ ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE);
+
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
+ float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats];
+
+ if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) {
+
+ uint8_t *data8 = (uint8_t *)dataptr;
+ data8[0] = CLAMP(p_custom_data.r * 255.0, 0, 255);
+ data8[1] = CLAMP(p_custom_data.g * 255.0, 0, 255);
+ data8[2] = CLAMP(p_custom_data.b * 255.0, 0, 255);
+ data8[3] = CLAMP(p_custom_data.a * 255.0, 0, 255);
+
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) {
+ dataptr[0] = p_custom_data.r;
+ dataptr[1] = p_custom_data.g;
+ dataptr[2] = p_custom_data.b;
+ dataptr[3] = p_custom_data.a;
+ }
+
+ multimesh->dirty_data = true;
+ multimesh->dirty_aabb = true;
+
+ if (!multimesh->update_list.in_list()) {
+ multimesh_update_list.add(&multimesh->update_list);
+ }
+}
RID RasterizerStorageGLES3::multimesh_get_mesh(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
@@ -4056,7 +4133,7 @@ Transform RasterizerStorageGLES3::multimesh_instance_get_transform(RID p_multime
ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform());
ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform());
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index];
Transform xform;
@@ -4083,7 +4160,7 @@ Transform2D RasterizerStorageGLES3::multimesh_instance_get_transform_2d(RID p_mu
ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform2D());
ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D());
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index];
Transform2D xform;
@@ -4105,7 +4182,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int
ERR_FAIL_INDEX_V(p_index, multimesh->size, Color());
ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color());
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats];
if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) {
@@ -4131,6 +4208,59 @@ Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int
return Color();
}
+Color RasterizerStorageGLES3::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
+
+ MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+ ERR_FAIL_COND_V(!multimesh, Color());
+ ERR_FAIL_INDEX_V(p_index, multimesh->size, Color());
+ ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color());
+
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
+ float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats];
+
+ if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) {
+ union {
+ uint32_t colu;
+ float colf;
+ } cu;
+
+ cu.colf = dataptr[0];
+
+ return Color::hex(BSWAP32(cu.colu));
+
+ } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) {
+ Color c;
+ c.r = dataptr[0];
+ c.g = dataptr[1];
+ c.b = dataptr[2];
+ c.a = dataptr[3];
+
+ return c;
+ }
+
+ return Color();
+}
+
+void RasterizerStorageGLES3::multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) {
+
+ MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+ ERR_FAIL_COND(!multimesh);
+
+ int dsize = multimesh->data.size();
+
+ ERR_FAIL_COND(dsize != p_array.size());
+
+ PoolVector<float>::Read r = p_array.read();
+ copymem(multimesh->data.ptrw(), r.ptr(), dsize * sizeof(float));
+
+ multimesh->dirty_data = true;
+ multimesh->dirty_aabb = true;
+
+ if (!multimesh->update_list.in_list()) {
+ multimesh_update_list.add(&multimesh->update_list);
+ }
+}
+
void RasterizerStorageGLES3::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
@@ -4179,7 +4309,7 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
mesh_aabb.size += Vector3(0.001, 0.001, 0.001);
}
- int stride = multimesh->color_floats + multimesh->xform_floats;
+ int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
int count = multimesh->data.size();
float *data = multimesh->data.ptrw();
@@ -7336,7 +7466,7 @@ void RasterizerStorageGLES3::initialize() {
{
//transform feedback buffers
- uint32_t xf_feedback_size = GLOBAL_DEF("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096);
+ uint32_t xf_feedback_size = GLOBAL_DEF_RST("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096);
for (int i = 0; i < 2; i++) {
glGenBuffers(1, &resources.transform_feedback_buffers[i]);
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 6b626cbd00..1db577f23c 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -268,6 +268,7 @@ public:
GLuint tex_id;
bool using_srgb;
+ bool redraw_if_visible;
uint16_t stored_cube_sides;
@@ -306,6 +307,7 @@ public:
detect_normal = NULL;
detect_normal_ud = NULL;
proxy = NULL;
+ redraw_if_visible = false;
}
_ALWAYS_INLINE_ Texture *get_ptr() {
@@ -366,6 +368,7 @@ public:
virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata);
virtual void texture_set_proxy(RID p_texture, RID p_proxy);
+ virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable);
/* SKY API */
@@ -690,7 +693,6 @@ public:
AABB custom_aabb;
mutable uint64_t last_pass;
SelfList<MultiMesh>::List multimeshes;
-
_FORCE_INLINE_ void update_multimeshes() {
SelfList<MultiMesh> *mm = multimeshes.first();
@@ -756,6 +758,7 @@ public:
int size;
VS::MultimeshTransformFormat transform_format;
VS::MultimeshColorFormat color_format;
+ VS::MultimeshCustomDataFormat custom_data_format;
Vector<float> data;
AABB aabb;
SelfList<MultiMesh> update_list;
@@ -765,6 +768,7 @@ public:
int xform_floats;
int color_floats;
+ int custom_data_floats;
bool dirty_aabb;
bool dirty_data;
@@ -776,11 +780,13 @@ public:
dirty_data = true;
xform_floats = 0;
color_floats = 0;
+ custom_data_floats = 0;
visible_instances = -1;
size = 0;
buffer = 0;
transform_format = VS::MULTIMESH_TRANSFORM_2D;
color_format = VS::MULTIMESH_COLOR_NONE;
+ custom_data_format = VS::MULTIMESH_CUSTOM_DATA_NONE;
}
};
@@ -792,19 +798,23 @@ public:
virtual RID multimesh_create();
- virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format);
+ virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data_format = VS::MULTIMESH_CUSTOM_DATA_NONE);
virtual int multimesh_get_instance_count(RID p_multimesh) const;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh);
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform);
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform);
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color);
+ virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color);
virtual RID multimesh_get_mesh(RID p_multimesh) const;
virtual Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const;
+ virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const;
+
+ virtual void multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array);
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
virtual int multimesh_get_visible_instances(RID p_multimesh) const;
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index eb8d6c485b..f3ba7aa408 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -780,8 +780,6 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL"] = "normal";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP"] = "normal_map";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP_DEPTH"] = "normal_depth";
- actions[VS::SHADER_CANVAS_ITEM].renames["UV"] = "uv_interp";
- actions[VS::SHADER_CANVAS_ITEM].renames["COLOR"] = "color";
actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE"] = "color_texture";
actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE_PIXEL_SIZE"] = "color_texpixel_size";
actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL_TEXTURE"] = "normal_texture";
@@ -824,7 +822,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp";
actions[VS::SHADER_SPATIAL].renames["COLOR"] = "color_interp";
actions[VS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize";
- //actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"]=ShaderLanguage::TYPE_INT;
+ actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "gl_InstanceID";
//builtins
@@ -846,13 +844,11 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss";
actions[VS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy";
actions[VS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow";
- //actions[VS::SHADER_SPATIAL].renames["SSS_SPREAD"] = "sss_spread";
actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength";
actions[VS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission";
actions[VS::SHADER_SPATIAL].renames["AO"] = "ao";
actions[VS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect";
actions[VS::SHADER_SPATIAL].renames["EMISSION"] = "emission";
- //actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"]=ShaderLanguage::TYPE_VEC2;
actions[VS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord";
actions[VS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom";
actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv";
@@ -894,10 +890,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
actions[VS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
- actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength";
-
actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
@@ -912,6 +907,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
/* PARTICLES SHADER */
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 326aab4c7c..e7828d265c 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -82,6 +82,7 @@ layout(std140) uniform LightData { //ubo:1
out vec4 light_uv_interp;
+out vec2 transformed_light_uv;
out vec4 local_rot;
@@ -236,6 +237,13 @@ VERTEX_SHADER_CODE
light_uv_interp.xy = (light_matrix * outvec).xy;
light_uv_interp.zw =(light_local_matrix * outvec).xy;
+
+ mat3 inverse_light_matrix = mat3(inverse(light_matrix));
+ inverse_light_matrix[0] = normalize(inverse_light_matrix[0]);
+ inverse_light_matrix[1] = normalize(inverse_light_matrix[1]);
+ inverse_light_matrix[2] = normalize(inverse_light_matrix[2]);
+ transformed_light_uv = (inverse_light_matrix * vec3(light_uv_interp.zw,0.0)).xy; //for normal mapping
+
#ifdef USE_SHADOWS
pos=outvec.xy;
#endif
@@ -304,6 +312,7 @@ layout(std140) uniform LightData {
uniform lowp sampler2D light_texture; // texunit:-1
in vec4 light_uv_interp;
+in vec2 transformed_light_uv;
in vec4 local_rot;
@@ -518,10 +527,9 @@ FRAGMENT_SHADER_CODE
-
#ifdef USE_LIGHTING
- vec2 light_vec = light_uv_interp.zw;; //for shadow and normal mapping
+ vec2 light_vec = transformed_light_uv;
if (normal_used) {
normal.xy = mat2(local_rot.xy,local_rot.zw) * normal.xy;
@@ -567,7 +575,7 @@ FRAGMENT_SHADER_CODE
color*=light;
#ifdef USE_SHADOWS
-
+ light_vec = light_uv_interp.zw; //for shadows
float angle_to_light = -atan(light_vec.x,light_vec.y);
float PI = 3.14159265358979323846264;
/*int i = int(mod(floor((angle_to_light+7.0*PI/6.0)/(4.0*PI/6.0))+1.0, 3.0)); // +1 pq os indices estao em ordem 2,0,1 nos arrays
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index f5481c597c..6fd85cc1dd 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -90,6 +90,8 @@ layout(std140) uniform SceneData { //ubo:0
mediump float reflection_multiplier;
mediump float subsurface_scatter_width;
mediump float ambient_occlusion_affect_light;
+ mediump float ambient_occlusion_affect_ao_channel;
+ mediump float opaque_prepass_threshold;
bool fog_depth_enabled;
highp float fog_depth_begin;
@@ -322,7 +324,13 @@ void main() {
#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
vertex = world_matrix * vertex;
+
+#if defined(ENSURE_CORRECT_NORMALS)
+ mat3 normal_matrix = mat3(transpose(inverse(world_matrix)));
+ normal = normal_matrix * normal;
+#else
normal = normalize((world_matrix * vec4(normal,0.0)).xyz);
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
@@ -394,7 +402,13 @@ VERTEX_SHADER_CODE
#if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED)
vertex = modelview * vertex;
+
+#if defined(ENSURE_CORRECT_NORMALS)
+ mat3 normal_matrix = mat3(transpose(inverse(modelview)));
+ normal = normal_matrix * normal;
+#else
normal = normalize((modelview * vec4(normal,0.0)).xyz);
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
@@ -558,11 +572,6 @@ in vec3 normal_interp;
/* PBR CHANNELS */
-//used on forward mainly
-uniform bool no_ambient_light;
-
-
-
#ifdef USE_RADIANCE_MAP
@@ -670,6 +679,8 @@ layout(std140) uniform SceneData {
mediump float reflection_multiplier;
mediump float subsurface_scatter_width;
mediump float ambient_occlusion_affect_light;
+ mediump float ambient_occlusion_affect_ao_channel;
+ mediump float opaque_prepass_threshold;
bool fog_depth_enabled;
highp float fog_depth_begin;
@@ -1017,12 +1028,11 @@ LIGHT_SHADER_CODE
diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
#endif
-#if defined(TRANSMISSION_USED)
- diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation;
-#else
diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation;
-#endif
+#if defined(TRANSMISSION_USED)
+ diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation;
+#endif
#if defined(LIGHT_USE_RIM)
@@ -1619,7 +1629,7 @@ void main() {
float alpha = 1.0;
#if defined(DO_SIDE_CHECK)
- float side=float(gl_FrontFacing)*2.0-1.0;
+ float side=gl_FrontFacing ? 1.0 : -1.0;
#else
float side=1.0;
#endif
@@ -1681,9 +1691,10 @@ FRAGMENT_SHADER_CODE
#ifdef USE_OPAQUE_PREPASS
- if (alpha<0.99) {
+ if (alpha<opaque_prepass_threshold) {
discard;
}
+
#endif
#if defined(ENABLE_NORMALMAP)
@@ -1738,42 +1749,43 @@ FRAGMENT_SHADER_CODE
#ifdef USE_RADIANCE_MAP
- if (no_ambient_light) {
- ambient_light=vec3(0.0,0.0,0.0);
- } else {
- {
-
- { //read radiance from dual paraboloid
+#ifdef AMBIENT_LIGHT_DISABLED
+ ambient_light=vec3(0.0,0.0,0.0);
+#else
+ {
- vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n);
- ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz);
- vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy;
- env_reflection_light = radiance;
+ { //read radiance from dual paraboloid
- }
- //no longer a cubemap
- //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
+ vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n);
+ ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz);
+ vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy;
+ env_reflection_light = radiance;
}
+ //no longer a cubemap
+ //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
+
+ }
#ifndef USE_LIGHTMAP
- {
+ {
- vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
- vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy;
+ vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
+ vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy;
- ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
- //ambient_light=vec3(0.0,0.0,0.0);
- }
-#endif
+ ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
+ //ambient_light=vec3(0.0,0.0,0.0);
}
+#endif
+#endif //AMBIENT_LIGHT_DISABLED
#else
- if (no_ambient_light){
- ambient_light=vec3(0.0,0.0,0.0);
- } else {
- ambient_light=ambient_light_color.rgb;
- }
+#ifdef AMBIENT_LIGHT_DISABLED
+ ambient_light=vec3(0.0,0.0,0.0);
+#else
+ ambient_light=ambient_light_color.rgb;
+#endif //AMBIENT_LIGHT_DISABLED
+
#endif
ambient_light*=ambient_energy;
@@ -2128,18 +2140,18 @@ FRAGMENT_SHADER_CODE
#else
-#if defined(ENABLE_AO)
- float ambient_scale=0.0; // AO is supplied by material
-#else
+
//approximate ambient scale for SSAO, since we will lack full ambient
float max_emission=max(emission.r,max(emission.g,emission.b));
float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b));
float max_diffuse=max(diffuse_light.r,max(diffuse_light.g,diffuse_light.b));
float total_ambient = max_ambient+max_diffuse+max_emission;
float ambient_scale = (total_ambient>0.0) ? (max_ambient+ambient_occlusion_affect_light*max_diffuse)/total_ambient : 0.0;
-#endif //ENABLE_AO
+#if defined(ENABLE_AO)
+ ambient_scale=mix(0.0,ambient_scale,ambient_occlusion_affect_ao_channel);
+#endif
diffuse_buffer=vec4(emission+diffuse_light+ambient_light,ambient_scale);
specular_buffer=vec4(specular_light,metallic);
@@ -2161,7 +2173,6 @@ FRAGMENT_SHADER_CODE
frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha);
#endif //SHADELESS
-
#endif //USE_MULTIPLE_RENDER_TARGETS
diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl
index a75871f08e..63475c9039 100644
--- a/drivers/gles3/shaders/tonemap.glsl
+++ b/drivers/gles3/shaders/tonemap.glsl
@@ -1,28 +1,27 @@
[vertex]
-
-layout(location=0) in highp vec4 vertex_attrib;
-layout(location=4) in vec2 uv_in;
+layout (location = 0) in highp vec4 vertex_attrib;
+layout (location = 4) in vec2 uv_in;
out vec2 uv_interp;
-void main() {
-
+void main()
+{
gl_Position = vertex_attrib;
+
uv_interp = uv_in;
-#ifdef V_FLIP
- uv_interp.y = 1.0-uv_interp.y;
-#endif
+ #ifdef V_FLIP
+ uv_interp.y = 1.0f - uv_interp.y;
+ #endif
}
[fragment]
#if !defined(GLES_OVER_GL)
-precision mediump float;
+ precision mediump float;
#endif
-
in vec2 uv_interp;
uniform highp sampler2D source; //texunit:0
@@ -31,297 +30,286 @@ uniform float exposure;
uniform float white;
#ifdef USE_AUTO_EXPOSURE
-
-uniform highp sampler2D source_auto_exposure; //texunit:1
-uniform highp float auto_exposure_grey;
-
+ uniform highp sampler2D source_auto_exposure; //texunit:1
+ uniform highp float auto_exposure_grey;
#endif
#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7)
+ #define USING_GLOW // only use glow when at least one glow level is selected
-uniform highp sampler2D source_glow; //texunit:2
-uniform highp float glow_intensity;
-
+ uniform highp sampler2D source_glow; //texunit:2
+ uniform highp float glow_intensity;
#endif
#ifdef USE_BCS
-
-uniform vec3 bcs;
-
+ uniform vec3 bcs;
#endif
#ifdef USE_COLOR_CORRECTION
-
-uniform sampler2D color_correction; //texunit:3
-
+ uniform sampler2D color_correction; //texunit:3
#endif
-
-layout(location = 0) out vec4 frag_color;
+layout (location = 0) out vec4 frag_color;
#ifdef USE_GLOW_FILTER_BICUBIC
+ // w0, w1, w2, and w3 are the four cubic B-spline basis functions
+ float w0(float a)
+ {
+ return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
+ }
-// w0, w1, w2, and w3 are the four cubic B-spline basis functions
-float w0(float a)
-{
- return (1.0/6.0)*(a*(a*(-a + 3.0) - 3.0) + 1.0);
-}
-
-float w1(float a)
-{
- return (1.0/6.0)*(a*a*(3.0*a - 6.0) + 4.0);
-}
-
-float w2(float a)
-{
- return (1.0/6.0)*(a*(a*(-3.0*a + 3.0) + 3.0) + 1.0);
-}
-
-float w3(float a)
-{
- return (1.0/6.0)*(a*a*a);
-}
-
-// g0 and g1 are the two amplitude functions
-float g0(float a)
-{
- return w0(a) + w1(a);
-}
+ float w1(float a)
+ {
+ return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
+ }
-float g1(float a)
-{
- return w2(a) + w3(a);
-}
+ float w2(float a)
+ {
+ return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
+ }
-// h0 and h1 are the two offset functions
-float h0(float a)
-{
- return -1.0 + w1(a) / (w0(a) + w1(a));
-}
+ float w3(float a)
+ {
+ return (1.0f / 6.0f) * (a * a * a);
+ }
-float h1(float a)
-{
- return 1.0 + w3(a) / (w2(a) + w3(a));
-}
+ // g0 and g1 are the two amplitude functions
+ float g0(float a)
+ {
+ return w0(a) + w1(a);
+ }
-uniform ivec2 glow_texture_size;
+ float g1(float a)
+ {
+ return w2(a) + w3(a);
+ }
-vec4 texture2D_bicubic(sampler2D tex, vec2 uv,int p_lod)
-{
- float lod=float(p_lod);
- vec2 tex_size = vec2(glow_texture_size >> p_lod);
- vec2 pixel_size =1.0/tex_size;
- uv = uv*tex_size + 0.5;
- vec2 iuv = floor( uv );
- vec2 fuv = fract( uv );
-
- float g0x = g0(fuv.x);
- float g1x = g1(fuv.x);
- float h0x = h0(fuv.x);
- float h1x = h1(fuv.x);
- float h0y = h0(fuv.y);
- float h1y = h1(fuv.y);
-
- vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size;
- vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size;
- vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size;
- vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size;
-
- return g0(fuv.y) * (g0x * textureLod(tex, p0,lod) +
- g1x * textureLod(tex, p1,lod)) +
- g1(fuv.y) * (g0x * textureLod(tex, p2,lod) +
- g1x * textureLod(tex, p3,lod));
-}
+ // h0 and h1 are the two offset functions
+ float h0(float a)
+ {
+ return -1.0f + w1(a) / (w0(a) + w1(a));
+ }
+ float h1(float a)
+ {
+ return 1.0f + w3(a) / (w2(a) + w3(a));
+ }
+ uniform ivec2 glow_texture_size;
-#define GLOW_TEXTURE_SAMPLE(m_tex,m_uv,m_lod) texture2D_bicubic(m_tex,m_uv,m_lod)
+ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod)
+ {
+ float lod = float(p_lod);
+ vec2 tex_size = vec2(glow_texture_size >> p_lod);
+ vec2 pixel_size = vec2(1.0f) / tex_size;
+
+ uv = uv * tex_size + vec2(0.5f);
+
+ vec2 iuv = floor(uv);
+ vec2 fuv = fract(uv);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
+ vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
+ vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
+ vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
+
+ return g0(fuv.y) * (g0x * textureLod(tex, p0,lod) +
+ g1x * textureLod(tex, p1,lod)) +
+ g1(fuv.y) * (g0x * textureLod(tex, p2,lod) +
+ g1x * textureLod(tex, p3,lod));
+ }
+ #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
#else
-
-#define GLOW_TEXTURE_SAMPLE(m_tex,m_uv,m_lod) textureLod(m_tex,m_uv,float(m_lod))
-
+ #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
#endif
+vec3 tonemap_filmic(vec3 color, float white)
+{
+ const float A = 0.15f;
+ const float B = 0.50f;
+ const float C = 0.10f;
+ const float D = 0.20f;
+ const float E = 0.02f;
+ const float F = 0.30f;
+ const float W = 11.2f;
+
+ vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
+ float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F;
+
+ return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f));
+}
-vec3 tonemap_filmic(vec3 color,float white) {
-
- float A = 0.15;
- float B = 0.50;
- float C = 0.10;
- float D = 0.20;
- float E = 0.02;
- float F = 0.30;
- float W = 11.2;
-
- vec3 coltn = ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F;
- float whitetn = ((white*(A*white+C*B)+D*E)/(white*(A*white+B)+D*F))-E/F;
+vec3 tonemap_aces(vec3 color, float white)
+{
+ const float A = 2.51f;
+ const float B = 0.03f;
+ const float C = 2.43f;
+ const float D = 0.59f;
+ const float E = 0.14f;
- return coltn/whitetn;
+ vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E);
+ float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E);
+ return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f));
}
-vec3 tonemap_aces(vec3 color) {
- float a = 2.51f;
- float b = 0.03f;
- float c = 2.43f;
- float d = 0.59f;
- float e = 0.14f;
- return color = clamp((color*(a*color+b))/(color*(c*color+d)+e),vec3(0.0),vec3(1.0));
+vec3 tonemap_reindhart(vec3 color, float white)
+{
+ return clamp((color) / (1.0f + color) * (1.0f + (color / (white))), vec3(0.0f), vec3(1.0f)); // whitepoint is probably not in linear space here!
}
-vec3 tonemap_reindhart(vec3 color,float white) {
-
- return ( color * ( 1.0 + ( color / ( white) ) ) ) / ( 1.0 + color );
+vec3 linear_to_srgb(vec3 color) // convert linear rgb to srgb, assumes clamped input in range [0;1]
+{
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
-void main() {
-
- vec4 color = textureLod(source, uv_interp, 0.0);
-
-#ifdef USE_AUTO_EXPOSURE
-
- color/=texelFetch(source_auto_exposure,ivec2(0,0),0).r/auto_exposure_grey;
-#endif
-
- color*=exposure;
-
-#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7)
-#define USING_GLOW
-#endif
-
-#if defined(USING_GLOW)
- vec3 glow = vec3(0.0);
-
-#ifdef USE_GLOW_LEVEL1
-
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,1).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL2
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,2).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL3
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,3).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL4
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,4).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL5
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,5).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL6
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,6).rgb;
-#endif
-
-#ifdef USE_GLOW_LEVEL7
- glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,7).rgb;
-#endif
-
-
- glow *= glow_intensity;
+vec3 apply_tonemapping(vec3 color, float white) // inputs are LINEAR, always outputs clamped [0;1] color
+{
+ #ifdef USE_REINDHART_TONEMAPPER
+ return tonemap_reindhart(color, white);
+ #endif
-#endif
+ #ifdef USE_FILMIC_TONEMAPPER
+ return tonemap_filmic(color, white);
+ #endif
+ #ifdef USE_ACES_TONEMAPPER
+ return tonemap_aces(color, white);
+ #endif
-#ifdef USE_REINDHART_TONEMAPPER
+ return clamp(color, vec3(0.0f), vec3(1.0f)); // no other seleced -> linear
+}
- color.rgb = tonemap_reindhart(color.rgb,white);
+vec3 gather_glow(sampler2D tex, vec2 uv) // sample all selected glow levels
+{
+ vec3 glow = vec3(0.0f);
-# if defined(USING_GLOW)
- glow = tonemap_reindhart(glow,white);
-# endif
+ #ifdef USE_GLOW_LEVEL1
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 1).rgb;
+ #endif
-#endif
+ #ifdef USE_GLOW_LEVEL2
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 2).rgb;
+ #endif
-#ifdef USE_FILMIC_TONEMAPPER
+ #ifdef USE_GLOW_LEVEL3
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 3).rgb;
+ #endif
- color.rgb = tonemap_filmic(color.rgb,white);
+ #ifdef USE_GLOW_LEVEL4
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 4).rgb;
+ #endif
-# if defined(USING_GLOW)
- glow = tonemap_filmic(glow,white);
-# endif
+ #ifdef USE_GLOW_LEVEL5
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 5).rgb;
+ #endif
-#endif
+ #ifdef USE_GLOW_LEVEL6
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb;
+ #endif
-#ifdef USE_ACES_TONEMAPPER
+ #ifdef USE_GLOW_LEVEL7
+ glow += GLOW_TEXTURE_SAMPLE(tex, uv, 7).rgb;
+ #endif
- color.rgb = tonemap_aces(color.rgb);
+ return glow;
+}
-# if defined(USING_GLOW)
- glow = tonemap_aces(glow);
-# endif
+vec3 apply_glow(vec3 color, vec3 glow) // apply glow using the selected blending mode
+{
+ #ifdef USE_GLOW_REPLACE
+ color = glow;
+ #endif
-#endif
+ #ifdef USE_GLOW_SCREEN
+ color = max((color + glow) - (color * glow), vec3(0.0));
+ #endif
-#ifdef KEEP_3D_LINEAR
- // leave color as is...
-#else
- //regular Linear -> SRGB conversion
- vec3 a = vec3(0.055);
- color.rgb = mix( (vec3(1.0)+a)*pow(color.rgb,vec3(1.0/2.4))-a , 12.92*color.rgb , lessThan(color.rgb,vec3(0.0031308)));
-#endif
+ #ifdef USE_GLOW_SOFTLIGHT
+ glow = glow * vec3(0.5f) + vec3(0.5f);
-#if defined(USING_GLOW)
- glow = mix( (vec3(1.0)+a)*pow(glow,vec3(1.0/2.4))-a , 12.92*glow , lessThan(glow,vec3(0.0031308)));
-#endif
+ color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
+ color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g)));
+ color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b)));
+ #endif
-//glow needs to be added in SRGB space (together with image space effects)
+ #if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive
+ color += glow;
+ #endif
- color.rgb = clamp(color.rgb,0.0,1.0);
+ return color;
+}
-#if defined(USING_GLOW)
- glow = clamp(glow,0.0,1.0);
-#endif
+vec3 apply_bcs(vec3 color, vec3 bcs)
+{
+ color = mix(vec3(0.0f), color, bcs.x);
+ color = mix(vec3(0.5f), color, bcs.y);
+ color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z);
-#ifdef USE_GLOW_REPLACE
+ return color;
+}
- color.rgb = glow;
+vec3 apply_color_correction(vec3 color, sampler2D correction_tex)
+{
+ color.r = texture(correction_tex, vec2(color.r, 0.0f)).r;
+ color.g = texture(correction_tex, vec2(color.g, 0.0f)).g;
+ color.b = texture(correction_tex, vec2(color.b, 0.0f)).b;
-#endif
+ return color;
+}
-#ifdef USE_GLOW_SCREEN
+void main()
+{
+ vec3 color = textureLod(source, uv_interp, 0.0f).rgb;
- color.rgb = max((color.rgb + glow) - (color.rgb * glow), vec3(0.0));
+ // Exposure
-#endif
+ #ifdef USE_AUTO_EXPOSURE
+ color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / auto_exposure_grey;
+ #endif
-#ifdef USE_GLOW_SOFTLIGHT
+ color *= exposure;
- {
+ // Early Tonemap & SRGB Conversion
- glow = (glow * 0.5) + 0.5;
- color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r)));
- color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g)));
- color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b)));
- }
+ color = apply_tonemapping(color, white);
-#endif
+ #ifdef KEEP_3D_LINEAR
+ // leave color as is (-> don't convert to SRGB)
+ #else
+ color = linear_to_srgb(color); // regular linear -> SRGB conversion
+ #endif
-#if defined(USING_GLOW) && !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE)
- //additive
- color.rgb+=glow;
-#endif
+ // Glow
-#ifdef USE_BCS
+ #ifdef USING_GLOW
+ vec3 glow = gather_glow(source_glow, uv_interp) * glow_intensity;
- color.rgb = mix(vec3(0.0),color.rgb,bcs.x);
- color.rgb = mix(vec3(0.5),color.rgb,bcs.y);
- color.rgb = mix(vec3(dot(vec3(1.0),color.rgb)*0.33333),color.rgb,bcs.z);
+ // high dynamic range -> SRGB
+ glow = apply_tonemapping(glow, white);
+ glow = linear_to_srgb(glow);
-#endif
+ color = apply_glow(color, glow);
+ #endif
-#ifdef USE_COLOR_CORRECTION
+ // Additional effects
- color.r = texture(color_correction,vec2(color.r,0.0)).r;
- color.g = texture(color_correction,vec2(color.g,0.0)).g;
- color.b = texture(color_correction,vec2(color.b,0.0)).b;
-#endif
+ #ifdef USE_BCS
+ color = apply_bcs(color, bcs);
+ #endif
+ #ifdef USE_COLOR_CORRECTION
+ color = apply_color_correction(color, color_correction);
+ #endif
- frag_color=vec4(color.rgb,1.0);
+ frag_color = vec4(color, 1.0f);
}
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 0f47949b4b..3622e48ecd 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -155,7 +155,7 @@ Error AudioDriverPulseAudio::init_device() {
break;
}
- int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
@@ -204,7 +204,7 @@ Error AudioDriverPulseAudio::init() {
thread_exited = false;
exit_thread = false;
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
pa_ml = pa_mainloop_new();
ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN);
@@ -289,18 +289,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
while (!ad->exit_thread) {
+
+ ad->lock();
+ ad->start_counting_ticks();
+
if (!ad->active) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out[i] = 0;
}
} else {
- ad->lock();
-
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
- ad->unlock();
-
if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out[i] = ad->samples_in[i] >> 16;
@@ -323,9 +323,6 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
int error_code;
int byte_size = ad->pa_buffer_size * sizeof(int16_t);
-
- ad->lock();
-
int ret;
do {
ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
@@ -350,11 +347,13 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
if (ret == 0) {
// If pa_mainloop_iterate returns 0 sleep for 1 msec to wait
// for the stream to be able to process more bytes
+ ad->stop_counting_ticks();
ad->unlock();
OS::get_singleton()->delay_usec(1000);
ad->lock();
+ ad->start_counting_ticks();
}
}
}
@@ -380,6 +379,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
}
}
+ ad->stop_counting_ticks();
ad->unlock();
}
@@ -453,7 +453,9 @@ String AudioDriverPulseAudio::get_device() {
void AudioDriverPulseAudio::set_device(String device) {
+ lock();
new_device = device;
+ unlock();
}
void AudioDriverPulseAudio::lock() {
diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp
index 457486797f..365788e192 100644
--- a/drivers/rtaudio/audio_driver_rtaudio.cpp
+++ b/drivers/rtaudio/audio_driver_rtaudio.cpp
@@ -88,7 +88,7 @@ Error AudioDriverRtAudio::init() {
// FIXME: Adapt to the OutputFormat -> SpeakerMode change
/*
- String channels = GLOBAL_DEF("audio/output","stereo");
+ String channels = GLOBAL_DEF_RST("audio/output","stereo");
if (channels=="5.1")
output_format=OUTPUT_5_1;
@@ -108,7 +108,7 @@ Error AudioDriverRtAudio::init() {
options.numberOfBuffers = 4;
parameters.firstChannel = 0;
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index e1680601ad..1d96f9ee7d 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -318,7 +318,7 @@ Error AudioDriverWASAPI::finish_device() {
Error AudioDriverWASAPI::init() {
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
Error err = init_device();
if (err != OK) {
@@ -335,22 +335,6 @@ Error AudioDriverWASAPI::init() {
return OK;
}
-Error AudioDriverWASAPI::reopen() {
- Error err = finish_device();
- if (err != OK) {
- ERR_PRINT("WASAPI: finish_device error");
- } else {
- err = init_device();
- if (err != OK) {
- ERR_PRINT("WASAPI: init_device error");
- } else {
- start();
- }
- }
-
- return err;
-}
-
int AudioDriverWASAPI::get_mix_rate() const {
return mix_rate;
@@ -416,7 +400,9 @@ String AudioDriverWASAPI::get_device() {
void AudioDriverWASAPI::set_device(String device) {
+ lock();
new_device = device;
+ unlock();
}
void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) {
@@ -453,24 +439,31 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata;
while (!ad->exit_thread) {
- if (ad->active) {
- ad->lock();
- ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
+ ad->lock();
+ ad->start_counting_ticks();
- ad->unlock();
+ if (ad->active) {
+ ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
} else {
for (unsigned int i = 0; i < ad->buffer_size; i++) {
ad->samples_in[i] = 0;
}
}
+ ad->stop_counting_ticks();
+ ad->unlock();
+
unsigned int left_frames = ad->buffer_frames;
unsigned int buffer_idx = 0;
while (left_frames > 0 && ad->audio_client) {
WaitForSingleObject(ad->event, 1000);
+ ad->lock();
+ ad->start_counting_ticks();
+
UINT32 cur_frames;
+ bool invalidated = false;
HRESULT hr = ad->audio_client->GetCurrentPadding(&cur_frames);
if (hr == S_OK) {
// Check how much frames are available on the WASAPI buffer
@@ -506,34 +499,34 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
left_frames -= write_frames;
} else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
- // Device is not valid anymore, reopen it
-
- Error err = ad->finish_device();
- if (err != OK) {
- ERR_PRINT("WASAPI: finish_device error");
- } else {
- // We reopened the device and samples_in may have resized, so invalidate the current left_frames
- left_frames = 0;
- }
+ invalidated = true;
} else {
ERR_PRINT("WASAPI: Get buffer error");
ad->exit_thread = true;
}
} else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
- // Device is not valid anymore, reopen it
+ invalidated = true;
+ } else {
+ ERR_PRINT("WASAPI: GetCurrentPadding error");
+ }
+
+ if (invalidated) {
+ // Device is not valid anymore
+ WARN_PRINT("WASAPI: Current device invalidated, closing device");
Error err = ad->finish_device();
if (err != OK) {
ERR_PRINT("WASAPI: finish_device error");
- } else {
- // We reopened the device and samples_in may have resized, so invalidate the current left_frames
- left_frames = 0;
}
- } else {
- ERR_PRINT("WASAPI: GetCurrentPadding error");
}
+
+ ad->stop_counting_ticks();
+ ad->unlock();
}
+ ad->lock();
+ ad->start_counting_ticks();
+
// If we're using the Default device and it changed finish it so we'll re-init the device
if (ad->device_name == "Default" && default_device_changed) {
Error err = ad->finish_device();
@@ -559,6 +552,9 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
ad->start();
}
}
+
+ ad->stop_counting_ticks();
+ ad->unlock();
}
ad->thread_exited = true;
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index c97f4c288c..f3ee5976eb 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -72,7 +72,6 @@ class AudioDriverWASAPI : public AudioDriver {
Error init_device(bool reinit = false);
Error finish_device();
- Error reopen();
public:
virtual const char *get_name() const {
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index aa0fd34e0a..ea194e5eae 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -141,9 +141,9 @@ void FileAccessWindows::close() {
bool rename_error = true;
int attempts = 4;
while (rename_error && attempts) {
- // This workaround of trying multiple times is added to deal with paranoid Windows
- // antiviruses that love reading just written files even if they are not executable, thus
- // locking the file and preventing renaming from happening.
+ // This workaround of trying multiple times is added to deal with paranoid Windows
+ // antiviruses that love reading just written files even if they are not executable, thus
+ // locking the file and preventing renaming from happening.
#ifdef UWP_ENABLED
// UWP has no PathFileExists, so we check attributes instead
diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index 6675459313..db108f9c6b 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -50,7 +50,7 @@ Error AudioDriverXAudio2::init() {
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
- int latency = GLOBAL_DEF("audio/output_latency", 25);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", 25);
buffer_size = closest_power_of_2(latency * mix_rate / 1000);
samples_in = memnew_arr(int32_t, buffer_size * channels);
diff --git a/editor/SCsub b/editor/SCsub
index c29da8dd8a..a9343f7f36 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -192,10 +192,10 @@ if env['tools']:
docs = sorted(docs)
env.Depends("#editor/doc_data_compressed.gen.h", docs)
- env.Command("#editor/doc_data_compressed.gen.h", docs, make_doc_header)
+ env.CommandNoCache("#editor/doc_data_compressed.gen.h", docs, make_doc_header)
# Certificates
env.Depends("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt")
- env.Command("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", make_certs_header)
+ env.CommandNoCache("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", make_certs_header)
import glob
path = env.Dir('.').abspath
@@ -203,13 +203,13 @@ if env['tools']:
# Translations
tlist = glob.glob(path + "/translations/*.po")
env.Depends('#editor/translations.gen.h', tlist)
- env.Command('#editor/translations.gen.h', tlist, make_translations_header)
+ env.CommandNoCache('#editor/translations.gen.h', tlist, make_translations_header)
# Fonts
flist = glob.glob(path + "/../thirdparty/fonts/*.ttf")
flist.append(glob.glob(path + "/../thirdparty/fonts/*.otf"))
env.Depends('#editor/builtin_fonts.gen.h', flist)
- env.Command('#editor/builtin_fonts.gen.h', flist, make_fonts_header)
+ env.CommandNoCache('#editor/builtin_fonts.gen.h', flist, make_fonts_header)
env.add_source_files(env.editor_sources, "*.cpp")
env.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"])
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 02d667031a..42d5ea120e 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* animation_track_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
#include "animation_track_editor.h"
#include "animation_track_editor_plugins.h"
#include "editor/animation_bezier_editor.h"
@@ -25,6 +55,7 @@ public:
ClassDB::bind_method("_update_obj", &AnimationTrackKeyEdit::_update_obj);
ClassDB::bind_method("_key_ofs_changed", &AnimationTrackKeyEdit::_key_ofs_changed);
ClassDB::bind_method("_hide_script_from_inspector", &AnimationTrackKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method("get_root_path", &AnimationTrackKeyEdit::get_root_path);
}
//PopupDialog *ke_dialog;
@@ -612,6 +643,10 @@ public:
_change_notify();
}
+ Node *get_root_path() {
+ return root_path;
+ }
+
AnimationTrackKeyEdit() {
hidden = true;
key_ofs = 0;
@@ -714,6 +749,7 @@ void AnimationTimelineEdit::_notification(int p_what) {
len_hb->set_position(Vector2(get_size().width - get_buttons_width(), 0));
len_hb->set_size(Size2(get_buttons_width(), get_size().height));
}
+
if (p_what == NOTIFICATION_DRAW) {
int key_range = get_size().width - get_buttons_width() - get_name_limit();
@@ -874,9 +910,11 @@ void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation) {
if (animation.is_valid()) {
len_hb->show();
add_track->show();
+ play_position->show();
} else {
len_hb->hide();
add_track->hide();
+ play_position->hide();
}
update();
update_values();
@@ -1663,7 +1701,7 @@ void AnimationTrackEdit::_path_entered(const String &p_text) {
String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
if (check_rect.has_point(p_pos)) {
- return TTR("Toggle this track on/off");
+ return TTR("Toggle this track on/off.");
}
if (path_rect.has_point(p_pos)) {
@@ -1671,7 +1709,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
}
if (update_mode_rect.has_point(p_pos)) {
- return TTR("Update Mode (How this property is set).");
+ return TTR("Update Mode (How this property is set)");
}
if (interp_mode_rect.has_point(p_pos)) {
@@ -1679,11 +1717,11 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
}
if (loop_mode_rect.has_point(p_pos)) {
- return TTR("Loop Wrap Mode (Interpolate end with beginning on loop");
+ return TTR("Loop Wrap Mode (Interpolate end with beginning on loop)");
}
if (remove_rect.has_point(p_pos)) {
- return TTR("Remove this track");
+ return TTR("Remove this track.");
}
if (p_pos.x >= timeline->get_name_limit() && p_pos.x <= (get_size().width - timeline->get_buttons_width())) {
@@ -2435,12 +2473,16 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
if (animation.is_valid()) {
animation->connect("changed", this, "_animation_changed");
+ hscroll->show();
+ edit->set_disabled(false);
step->set_block_signals(true);
step->set_value(animation->get_step());
step->set_block_signals(false);
step->set_read_only(false);
snap->set_disabled(false);
} else {
+ hscroll->hide();
+ edit->set_disabled(true);
step->set_block_signals(true);
step->set_value(0);
step->set_block_signals(false);
@@ -3416,7 +3458,6 @@ MenuButton *AnimationTrackEditor::get_edit_menu() {
void AnimationTrackEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
-
zoom_icon->set_texture(get_icon("Zoom", "EditorIcons"));
snap->set_icon(get_icon("Snap", "EditorIcons"));
view_group->set_icon(get_icon(view_group->is_pressed() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons"));
@@ -3429,7 +3470,6 @@ void AnimationTrackEditor::_notification(int p_what) {
}
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
-
update_keying();
EditorNode::get_singleton()->update_keying();
emit_signal("keying_changed");
@@ -4808,9 +4848,10 @@ AnimationTrackEditor::AnimationTrackEditor() {
timeline_vbox->set_custom_minimum_size(Size2(0, 150) * EDSCALE);
hscroll = memnew(HScrollBar);
- timeline_vbox->add_child(hscroll);
hscroll->share(timeline);
+ hscroll->hide();
hscroll->connect("value_changed", this, "_update_scroll");
+ timeline_vbox->add_child(hscroll);
timeline->set_hscroll(hscroll);
track_vbox = memnew(VBoxContainer);
@@ -4853,6 +4894,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
step->set_step(0.01);
step->set_hide_slider(true);
step->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
+ step->set_tooltip(TTR("Animation step value."));
bottom_hb->add_child(step);
step->connect("value_changed", this, "_update_step");
step->set_read_only(true);
@@ -4875,6 +4917,8 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit = memnew(MenuButton);
edit->set_text(TTR("Edit"));
edit->set_flat(false);
+ edit->set_disabled(true);
+ edit->set_tooltip(TTR("Animation properties."));
edit->get_popup()->add_item(TTR("Copy Tracks"), EDIT_COPY_TRACKS);
edit->get_popup()->add_item(TTR("Paste Tracks"), EDIT_PASTE_TRACKS);
edit->get_popup()->add_separator();
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 665ce7658f..6aec6135f1 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -95,7 +95,7 @@ void FindReplaceBar::_notification(int p_what) {
set_process_unhandled_input(is_visible_in_tree());
if (is_visible_in_tree()) {
- call_deferred("_update_size");
+ _update_size();
}
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
@@ -775,7 +775,7 @@ void CodeTextEditor::update_editor_settings() {
text_editor->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_current_line"));
text_editor->cursor_set_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink"));
text_editor->cursor_set_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed"));
- text_editor->set_draw_breakpoint_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_breakpoint_gutter"));
+ text_editor->set_breakpoint_gutter_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_breakpoint_gutter"));
text_editor->set_hiding_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding"));
text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding"));
text_editor->set_wrap_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/word_wrap"));
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 7f93917744..c4f4e28fec 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -428,6 +428,13 @@ void ConnectionsDock::_make_or_edit_connection() {
bool oshot = connect_dialog->get_oneshot();
cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0);
+ bool add_script_function = connect_dialog->get_make_callback();
+ PoolStringArray script_function_args;
+ if (add_script_function) {
+ // pick up args here before "it" is deleted by update_tree
+ script_function_args = it->get_metadata(0).operator Dictionary()["args"];
+ }
+
if (connect_dialog->is_editing()) {
_disconnect(*it);
_connect(cToMake);
@@ -435,9 +442,12 @@ void ConnectionsDock::_make_or_edit_connection() {
_connect(cToMake);
}
- if (connect_dialog->get_make_callback()) {
- PoolStringArray args = it->get_metadata(0).operator Dictionary()["args"];
- editor->emit_signal("script_add_function_request", target, cToMake.method, args);
+ // IMPORTANT NOTE: _disconnect and _connect cause an update_tree,
+ // which will delete the object "it" is pointing to
+ it = NULL;
+
+ if (add_script_function) {
+ editor->emit_signal("script_add_function_request", target, cToMake.method, script_function_args);
hide();
}
@@ -810,7 +820,9 @@ void ConnectionsDock::update_tree() {
if (i > 0)
signaldesc += ", ";
String tname = "var";
- if (pi.type != Variant::NIL) {
+ if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
+ tname = pi.class_name.operator String();
+ } else if (pi.type != Variant::NIL) {
tname = Variant::get_type_name(pi.type);
}
signaldesc += tname + " " + (pi.name == "" ? String("arg " + itos(i)) : pi.name);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 36978e37a5..6b2a072e20 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -243,6 +243,18 @@ void CreateDialog::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+
+ Map<String, List<String> > global_class_map;
+ for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
+ String base = ScriptServer::get_global_class_base(E->get());
+ if (!global_class_map.has(base)) {
+ global_class_map[base] = List<String>();
+ }
+ global_class_map[base].push_back(E->get());
+ }
+
HashMap<String, TreeItem *> types;
TreeItem *root = search_options->create_item();
@@ -262,13 +274,17 @@ void CreateDialog::_update_search() {
if (base_type == "Node" && type.begins_with("Editor"))
continue; // do not show editor nodes
- if (base_type == "Resource" && ClassDB::is_parent_class(type, "PluginScript"))
- // PluginScript must be initialized before use, which is not possible here
- continue;
-
if (!ClassDB::can_instance(type))
continue; // can't create what can't be instanced
+ bool skip = false;
+ for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) {
+ if (ClassDB::is_parent_class(type, E->get()))
+ skip = true;
+ }
+ if (skip)
+ continue;
+
if (search_box->get_text() == "") {
add_type(type, types, root, &to_select);
} else {
@@ -289,6 +305,32 @@ void CreateDialog::_update_search() {
add_type(I->get(), types, root, &to_select);
}
+ if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) {
+ for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) {
+ bool show = search_box->get_text().is_subsequence_ofi(J->get());
+
+ if (!show)
+ continue;
+
+ if (!types.has(type))
+ add_type(type, types, root, &to_select);
+
+ TreeItem *ti;
+ if (types.has(type))
+ ti = types[type];
+ else
+ ti = search_options->get_root();
+
+ TreeItem *item = search_options->create_item(ti);
+ item->set_metadata(0, J->get());
+ item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")");
+ item->set_icon(0, _get_editor_icon(type));
+ if (!to_select || J->get() == search_box->get_text()) {
+ to_select = item;
+ }
+ }
+ }
+
if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) {
//there are custom types based on this... cool.
@@ -440,6 +482,17 @@ Object *CreateDialog::instance_selected() {
custom = md;
if (custom != String()) {
+
+ if (ScriptServer::is_global_class(custom)) {
+ RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom));
+ ERR_FAIL_COND_V(!script.is_valid(), NULL);
+
+ Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom));
+ ERR_FAIL_COND_V(!obj, NULL);
+
+ obj->set_script(script.get_ref_ptr());
+ return obj;
+ }
return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
} else {
return ClassDB::instance(selected->get_text(0));
@@ -706,4 +759,7 @@ CreateDialog::CreateDialog() {
help_bit = memnew(EditorHelpBit);
vbc->add_margin_child(TTR("Description:"), help_bit);
help_bit->connect("request_hide", this, "_closed");
+
+ type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here
+ type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix.
}
diff --git a/editor/create_dialog.h b/editor/create_dialog.h
index da17dcbe89..f8eec231a4 100644
--- a/editor/create_dialog.h
+++ b/editor/create_dialog.h
@@ -58,6 +58,7 @@ class CreateDialog : public ConfirmationDialog {
String preferred_search_result_type;
EditorHelpBit *help_bit;
List<StringName> type_list;
+ Set<StringName> type_blacklist;
void _item_selected();
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 30983ca149..2f0982e5d9 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -753,7 +753,6 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
autoload_cache.push_back(info);
}
- List<Node *> to_add;
for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) {
AutoLoadInfo &info = E->get();
@@ -763,9 +762,6 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
Ref<Script> scr = info.node->get_script();
info.in_editor = scr.is_valid() && scr->is_tool();
info.node->set_name(info.name);
- if (info.in_editor) {
- to_add.push_back(info.node);
- }
}
if (info.is_singleton) {
@@ -780,10 +776,6 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
}
}
- for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) {
- get_tree()->get_root()->add_child(E->get());
- }
-
autoload_changed = "autoload_changed";
updating_autoload = false;
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 7739b08eff..317fffad64 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -409,6 +409,7 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S
String cur_dir = da->get_current_dir().replace("\\", "/");
if (!cur_dir.ends_with("/"))
cur_dir += "/";
+ String cur_dir_no_prefix = cur_dir.replace("res://", "");
Vector<String> dirs;
String f;
@@ -417,8 +418,10 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S
dirs.push_back(f);
else {
String fullpath = cur_dir + f;
+ // Test also against path without res:// so that filters like `file.txt` can work.
+ String fullpath_no_prefix = cur_dir_no_prefix + f;
for (int i = 0; i < p_filters.size(); ++i) {
- if (fullpath.matchn(p_filters[i])) {
+ if (fullpath.matchn(p_filters[i]) || fullpath_no_prefix.matchn(p_filters[i])) {
if (!exclude) {
r_list.insert(fullpath);
} else {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index d8ae1da72e..d8ab41fa05 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -125,6 +125,14 @@ bool EditorFileSystemDirectory::get_file_import_is_valid(int p_idx) const {
return files[p_idx]->import_valid;
}
+String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const {
+ return files[p_idx]->script_class_name;
+}
+
+String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const {
+ return files[p_idx]->script_class_extends;
+}
+
StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, files.size(), "");
@@ -149,6 +157,8 @@ void EditorFileSystemDirectory::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_file", "idx"), &EditorFileSystemDirectory::get_file);
ClassDB::bind_method(D_METHOD("get_file_path", "idx"), &EditorFileSystemDirectory::get_file_path);
ClassDB::bind_method(D_METHOD("get_file_type", "idx"), &EditorFileSystemDirectory::get_file_type);
+ ClassDB::bind_method(D_METHOD("get_file_script_class_name", "idx"), &EditorFileSystemDirectory::get_file_script_class_name);
+ ClassDB::bind_method(D_METHOD("get_file_script_class_extends", "idx"), &EditorFileSystemDirectory::get_file_script_class_extends);
ClassDB::bind_method(D_METHOD("get_file_import_is_valid", "idx"), &EditorFileSystemDirectory::get_file_import_is_valid);
ClassDB::bind_method(D_METHOD("get_name"), &EditorFileSystemDirectory::get_name);
ClassDB::bind_method(D_METHOD("get_path"), &EditorFileSystemDirectory::get_path);
@@ -189,7 +199,7 @@ void EditorFileSystem::_scan_filesystem() {
String project = ProjectSettings::get_singleton()->get_resource_path();
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::READ);
if (f) {
@@ -209,7 +219,7 @@ void EditorFileSystem::_scan_filesystem() {
} else {
Vector<String> split = l.split("::");
- ERR_CONTINUE(split.size() != 6);
+ ERR_CONTINUE(split.size() != 7);
String name = split[0];
String file;
@@ -221,8 +231,10 @@ void EditorFileSystem::_scan_filesystem() {
fc.modification_time = split[2].to_int64();
fc.import_modification_time = split[3].to_int64();
fc.import_valid = split[4].to_int64() != 0;
+ fc.script_class_name = split[5].get_slice("<>", 0);
+ fc.script_class_extends = split[5].get_slice("<>", 1);
- String deps = split[5].strip_edges();
+ String deps = split[6].strip_edges();
if (deps.length()) {
Vector<String> dp = deps.split("<>");
for (int i = 0; i < dp.size(); i++) {
@@ -239,7 +251,7 @@ void EditorFileSystem::_scan_filesystem() {
memdelete(f);
}
- String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
+ String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
if (FileAccess::exists(update_cache)) {
{
@@ -287,7 +299,7 @@ void EditorFileSystem::_scan_filesystem() {
}
void EditorFileSystem::_save_filesystem_cache() {
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE);
if (f == NULL) {
@@ -563,6 +575,7 @@ void EditorFileSystem::scan() {
scanning = false;
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
} else {
@@ -706,6 +719,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->modified_time = fc->modification_time;
fi->import_modified_time = fc->import_modification_time;
fi->import_valid = fc->import_valid;
+ fi->script_class_name = fc->script_class_name;
+ fi->script_class_extends = fc->script_class_extends;
+
if (fc->type == String()) {
fi->type = ResourceLoader::get_resource_type(path);
//there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?)
@@ -715,6 +731,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
} else {
fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->modified_time = 0;
fi->import_modified_time = 0;
fi->import_valid = ResourceLoader::is_import_valid(path);
@@ -734,9 +751,12 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->deps = fc->deps;
fi->import_modified_time = 0;
fi->import_valid = true;
+ fi->script_class_name = fc->script_class_name;
+ fi->script_class_extends = fc->script_class_extends;
} else {
//new or modified time
fi->type = ResourceLoader::get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->deps = _get_dependencies(path);
fi->modified_time = mt;
fi->import_modified_time = 0;
@@ -835,6 +855,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
fi->modified_time = FileAccess::get_modified_time(path);
fi->import_modified_time = 0;
fi->type = ResourceLoader::get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->import_valid = ResourceLoader::is_import_valid(path);
{
@@ -1044,6 +1065,7 @@ void EditorFileSystem::_notification(int p_what) {
if (_update_scan_actions())
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
}
} else if (!scanning) {
@@ -1059,6 +1081,7 @@ void EditorFileSystem::_notification(int p_what) {
_update_scan_actions();
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
}
}
} break;
@@ -1087,7 +1110,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
for (int i = 0; i < p_dir->files.size(); i++) {
- String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid);
+ String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends;
s += "::";
for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
@@ -1268,7 +1291,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p
void EditorFileSystem::_save_late_updated_files() {
//files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
FileAccessRef f = FileAccess::open(fscache, FileAccess::WRITE);
for (Set<String>::Element *E = late_update_files.front(); E; E = E->next()) {
f->store_line(E->get());
@@ -1293,6 +1316,67 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
return ret;
}
+String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
+ String global_name;
+ String extends;
+
+ global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends);
+ *r_extends = extends;
+ return global_name;
+ }
+ }
+ *r_extends = String();
+ return String();
+}
+
+void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
+ int filecount = p_dir->files.size();
+ const EditorFileSystemDirectory::FileInfo *const *files = p_dir->files.ptr();
+ for (int i = 0; i < filecount; i++) {
+ if (files[i]->script_class_name == String()) {
+ continue;
+ }
+
+ String lang;
+ for (int j = 0; j < ScriptServer::get_language_count(); j++) {
+ if (ScriptServer::get_language(j)->handles_global_class_type(files[i]->type)) {
+ lang = ScriptServer::get_language(j)->get_name();
+ }
+ }
+
+ ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
+ }
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _scan_script_classes(p_dir->get_subdir(i));
+ }
+}
+
+void EditorFileSystem::update_script_classes() {
+
+ if (!update_script_classes_queued)
+ return;
+
+ update_script_classes_queued = false;
+ ScriptServer::global_classes_clear();
+ if (get_filesystem()) {
+ _scan_script_classes(get_filesystem());
+ }
+
+ ScriptServer::save_global_classes();
+}
+
+void EditorFileSystem::_queue_update_script_classes() {
+ if (update_script_classes_queued) {
+ return;
+ }
+
+ update_script_classes_queued = true;
+ call_deferred("update_script_classes");
+}
+
void EditorFileSystem::update_file(const String &p_file) {
EditorFileSystemDirectory *fs = NULL;
@@ -1311,7 +1395,9 @@ void EditorFileSystem::update_file(const String &p_file) {
memdelete(fs->files[cpos]);
fs->files.remove(cpos);
}
+
call_deferred("emit_signal", "filesystem_changed"); //update later
+ _queue_update_script_classes();
return;
}
@@ -1351,6 +1437,7 @@ void EditorFileSystem::update_file(const String &p_file) {
}
fs->files[cpos]->type = type;
+ fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends);
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->deps = _get_dependencies(p_file);
fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);
@@ -1359,6 +1446,7 @@ void EditorFileSystem::update_file(const String &p_file) {
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
call_deferred("emit_signal", "filesystem_changed"); //update later
+ _queue_update_script_classes();
}
void EditorFileSystem::_reimport_file(const String &p_file) {
@@ -1611,6 +1699,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_file", "path"), &EditorFileSystem::update_file);
ClassDB::bind_method(D_METHOD("get_filesystem_path", "path"), &EditorFileSystem::get_filesystem_path);
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
+ ClassDB::bind_method(D_METHOD("update_script_classes"), &EditorFileSystem::update_script_classes);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
@@ -1664,6 +1753,7 @@ EditorFileSystem::EditorFileSystem() {
memdelete(da);
scan_total = 0;
+ update_script_classes_queued = false;
}
EditorFileSystem::~EditorFileSystem() {
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index a587d2879a..1aa35f4782 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -58,6 +58,8 @@ class EditorFileSystemDirectory : public Object {
bool import_valid;
Vector<String> deps;
bool verified; //used for checking changes
+ String script_class_name;
+ String script_class_extends;
};
struct FileInfoSort {
@@ -86,6 +88,8 @@ public:
StringName get_file_type(int p_idx) const;
Vector<String> get_file_deps(int p_idx) const;
bool get_file_import_is_valid(int p_idx) const;
+ String get_file_script_class_name(int p_idx) const; //used for scripts
+ String get_file_script_class_extends(int p_idx) const; //used for scripts
EditorFileSystemDirectory *get_parent();
@@ -157,6 +161,8 @@ class EditorFileSystem : public Node {
uint64_t import_modification_time;
Vector<String> deps;
bool import_valid;
+ String script_class_name;
+ String script_class_extends;
};
HashMap<String, FileCache> file_cache;
@@ -215,6 +221,12 @@ class EditorFileSystem : public Node {
}
};
+ void _scan_script_classes(EditorFileSystemDirectory *p_dir);
+ volatile bool update_script_classes_queued;
+ void _queue_update_script_classes();
+
+ String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -237,6 +249,8 @@ public:
void reimport_files(const Vector<String> &p_files);
+ void update_script_classes();
+
EditorFileSystem();
~EditorFileSystem();
};
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 26f16e282e..8e0d92267c 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -31,6 +31,7 @@
#include "editor_fonts.h"
#include "builtin_fonts.gen.h"
+#include "core/os/dir_access.h"
#include "editor_scale.h"
#include "editor_settings.h"
#include "scene/resources/default_theme/default_theme.h"
@@ -114,28 +115,34 @@ static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p
MAKE_FALLBACKS(m_name);
void editor_register_fonts(Ref<Theme> p_theme) {
+ DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
/* Custom font */
DynamicFontData::Hinting font_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/main_font_hinting");
String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
Ref<DynamicFontData> CustomFont;
- if (custom_font_path.length() > 0) {
+ if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
CustomFont.instance();
CustomFont->set_hinting(font_hinting);
CustomFont->set_font_path(custom_font_path);
CustomFont->set_force_autohinter(true); //just looks better..i think?
+ } else {
+ EditorSettings::get_singleton()->set_manually("interface/editor/main_font", "");
}
/* Custom Bold font */
String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold");
Ref<DynamicFontData> CustomFontBold;
- if (custom_font_path_bold.length() > 0) {
+ if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
CustomFontBold.instance();
CustomFontBold->set_hinting(font_hinting);
CustomFontBold->set_font_path(custom_font_path_bold);
CustomFontBold->set_force_autohinter(true); //just looks better..i think?
+ } else {
+ EditorSettings::get_singleton()->set_manually("interface/editor/main_font_bold", "");
}
/* Custom source code font */
@@ -143,12 +150,16 @@ void editor_register_fonts(Ref<Theme> p_theme) {
String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
DynamicFontData::Hinting font_source_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/code_font_hinting");
Ref<DynamicFontData> CustomFontSource;
- if (custom_font_path_source.length() > 0) {
+ if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) {
CustomFontSource.instance();
CustomFontSource->set_hinting(font_source_hinting);
CustomFontSource->set_font_path(custom_font_path_source);
+ } else {
+ EditorSettings::get_singleton()->set_manually("interface/editor/code_font", "");
}
+ memdelete(dir);
+
/* Droid Sans */
Ref<DynamicFontData> DefaultFont;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 65e50560bc..727383b960 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1900,6 +1900,7 @@ void EditorHelpBit::_meta_clicked(String p_select) {
void EditorHelpBit::_bind_methods() {
ClassDB::bind_method("_meta_clicked", &EditorHelpBit::_meta_clicked);
+ ClassDB::bind_method(D_METHOD("set_text", "text"), &EditorHelpBit::set_text);
ADD_SIGNAL(MethodInfo("request_hide"));
}
@@ -1925,7 +1926,7 @@ EditorHelpBit::EditorHelpBit() {
rich_text = memnew(RichTextLabel);
add_child(rich_text);
- rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ //rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE);
rich_text->connect("meta_clicked", this, "_meta_clicked");
rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor"));
rich_text->set_override_selected_font_color(false);
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 514169dc19..dbea97e98b 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -272,9 +272,9 @@ public:
~EditorHelp();
};
-class EditorHelpBit : public Panel {
+class EditorHelpBit : public PanelContainer {
- GDCLASS(EditorHelpBit, Panel);
+ GDCLASS(EditorHelpBit, PanelContainer);
RichTextLabel *rich_text;
void _go_to_help(String p_what);
@@ -285,6 +285,7 @@ protected:
void _notification(int p_what);
public:
+ RichTextLabel *get_rich_text() { return rich_text; }
void set_text(const String &p_text);
EditorHelpBit();
};
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 9a3174fb1a..488980db07 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -91,8 +91,11 @@ void EditorProperty::_notification(int p_what) {
Rect2 rect;
Rect2 bottom_rect;
+ right_child_rect = Rect2();
+ bottom_child_rect = Rect2();
+
{
- int child_room = size.width / 2;
+ int child_room = size.width * (1.0 - split_ratio);
Ref<Font> font = get_font("font", "Tree");
int height = font->get_height();
@@ -118,7 +121,8 @@ void EditorProperty::_notification(int p_what) {
if (bottom_editor) {
- int m = get_constant("item_margin", "Tree");
+ int m = 0; //get_constant("item_margin", "Tree");
+
bottom_rect = Rect2(m, rect.size.height + get_constant("vseparation", "Tree"), size.width - m, bottom_editor->get_combined_minimum_size().height);
}
}
@@ -147,10 +151,12 @@ void EditorProperty::_notification(int p_what) {
continue;
fit_child_in_rect(c, rect);
+ right_child_rect = rect;
}
if (bottom_editor) {
fit_child_in_rect(bottom_editor, bottom_rect);
+ bottom_child_rect = bottom_rect;
}
update(); //need to redraw text
@@ -158,6 +164,7 @@ void EditorProperty::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
Ref<Font> font = get_font("font", "Tree");
+ Color dark_color = get_color("dark_color_2", "Editor");
Size2 size = get_size();
if (bottom_editor) {
@@ -171,6 +178,13 @@ void EditorProperty::_notification(int p_what) {
draw_style_box(sb, Rect2(Vector2(), size));
}
+ if (right_child_rect != Rect2()) {
+ draw_rect(right_child_rect, dark_color);
+ }
+ if (bottom_child_rect != Rect2()) {
+ draw_rect(bottom_child_rect, dark_color);
+ }
+
Color color;
if (draw_red) {
color = get_color("error_color", "Editor");
@@ -251,7 +265,7 @@ void EditorProperty::_notification(int p_what) {
//int vs = get_constant("vseparation", "Tree");
Color guide_color = get_color("guide_color", "Tree");
int vs_height = get_size().height; // vs / 2;
- draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color);
+ // draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color);
}
}
@@ -691,11 +705,38 @@ bool EditorProperty::is_selectable() const {
return selectable;
}
+void EditorProperty::set_name_split_ratio(float p_ratio) {
+ split_ratio = p_ratio;
+}
+
+float EditorProperty::get_name_split_ratio() const {
+
+ return split_ratio;
+}
+
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
}
+Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
+
+ tooltip_text = p_text;
+ EditorHelpBit *help_bit = memnew(EditorHelpBit);
+ help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
+ help_bit->get_rich_text()->set_fixed_size_to_width(300);
+
+ String text = TTR("Property: ") + "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n";
+ text += p_text.get_slice("::", 1).strip_edges();
+ help_bit->set_text(text);
+ help_bit->call_deferred("set_text", text); //hack so it uses proper theme once inside scene
+ return help_bit;
+}
+
+String EditorProperty::get_tooltip_text() const {
+ return tooltip_text;
+}
+
void EditorProperty::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_label", "text"), &EditorProperty::set_label);
@@ -722,6 +763,8 @@ void EditorProperty::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input);
ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused);
+ ClassDB::bind_method(D_METHOD("get_tooltip_text"), &EditorProperty::get_tooltip_text);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checkable"), "set_checkable", "is_checkable");
@@ -744,6 +787,7 @@ void EditorProperty::_bind_methods() {
EditorProperty::EditorProperty() {
+ split_ratio = 0.5;
selectable = true;
text_size = 0;
read_only = false;
@@ -895,6 +939,20 @@ void EditorInspectorCategory::_notification(int p_what) {
}
}
+Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const {
+
+ tooltip_text = p_text;
+ EditorHelpBit *help_bit = memnew(EditorHelpBit);
+ help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
+ help_bit->get_rich_text()->set_fixed_size_to_width(300);
+
+ String text = "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n";
+ text += p_text.get_slice("::", 1).strip_edges();
+ help_bit->set_text(text);
+ help_bit->call_deferred("set_text", text); //hack so it uses proper theme once inside scene
+ return help_bit;
+}
+
Size2 EditorInspectorCategory::get_minimum_size() const {
Ref<Font> font = get_font("font", "Tree");
@@ -910,12 +968,29 @@ Size2 EditorInspectorCategory::get_minimum_size() const {
return ms;
}
+void EditorInspectorCategory::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_tooltip_text"), &EditorInspectorCategory::get_tooltip_text);
+}
+
+String EditorInspectorCategory::get_tooltip_text() const {
+
+ return tooltip_text;
+}
+
EditorInspectorCategory::EditorInspectorCategory() {
}
////////////////////////////////////////////////
////////////////////////////////////////////////
+void EditorInspectorSection::_test_unfold() {
+
+ if (!vbox_added) {
+ add_child(vbox);
+ vbox_added = true;
+ }
+}
+
void EditorInspectorSection::_notification(int p_what) {
if (p_what == NOTIFICATION_SORT_CHILDREN) {
@@ -926,9 +1001,9 @@ void EditorInspectorSection::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (foldable) {
if (object->editor_is_section_unfolded(section)) {
- arrow = get_icon("arrow", "Tree");
+ arrow = get_icon("arrow_up", "Tree");
} else {
- arrow = get_icon("arrow_collapsed", "Tree");
+ arrow = get_icon("arrow", "Tree");
}
}
#endif
@@ -941,7 +1016,7 @@ void EditorInspectorSection::_notification(int p_what) {
}
offset.y += get_constant("vseparation", "Tree");
- offset.x += get_constant("item_margin", "Tree");
+ offset.x += get_constant("inspector_margin", "Editor");
Rect2 rect(offset, size - offset);
@@ -969,9 +1044,9 @@ void EditorInspectorSection::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (foldable) {
if (object->editor_is_section_unfolded(section)) {
- arrow = get_icon("arrow", "Tree");
+ arrow = get_icon("arrow_up", "Tree");
} else {
- arrow = get_icon("arrow_collapsed", "Tree");
+ arrow = get_icon("arrow", "Tree");
}
}
#endif
@@ -988,14 +1063,14 @@ void EditorInspectorSection::_notification(int p_what) {
int hs = get_constant("hseparation", "Tree");
+ Color color = get_color("font_color", "Tree");
+ draw_string(font, Point2(hs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
+
int ofs = 0;
if (arrow.is_valid()) {
- draw_texture(arrow, Point2(ofs, (h - arrow->get_height()) / 2).floor());
+ draw_texture(arrow, Point2(get_size().width - arrow->get_width(), (h - arrow->get_height()) / 2).floor());
ofs += hs + arrow->get_width();
}
-
- Color color = get_color("font_color", "Tree");
- draw_string(font, Point2(ofs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
}
}
@@ -1017,8 +1092,8 @@ Size2 EditorInspectorSection::get_minimum_size() const {
}
Ref<Font> font = get_font("font", "Tree");
- ms.height += font->get_ascent() + get_constant("vseparation", "Tree");
- ms.width += get_constant("item_margin", "Tree");
+ ms.height += font->get_height() + get_constant("vseparation", "Tree");
+ ms.width += get_constant("inspector_margin", "Editor");
return ms;
}
@@ -1031,16 +1106,20 @@ void EditorInspectorSection::setup(const String &p_section, const String &p_labe
bg_color = p_bg_color;
foldable = p_foldable;
+ if (!foldable && !vbox_added) {
+ add_child(vbox);
+ vbox_added = true;
+ }
+
#ifdef TOOLS_ENABLED
if (foldable) {
+ _test_unfold();
if (object->editor_is_section_unfolded(section)) {
vbox->show();
} else {
vbox->hide();
}
}
- // void editor_set_section_unfold(const String &p_section, bool p_unfolded);
-
#endif
}
@@ -1053,6 +1132,9 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ _test_unfold();
+
bool unfold = !object->editor_is_section_unfolded(section);
object->editor_set_section_unfold(section, unfold);
if (unfold) {
@@ -1072,6 +1154,9 @@ void EditorInspectorSection::unfold() {
if (!foldable)
return;
+
+ _test_unfold();
+
#ifdef TOOLS_ENABLED
object->editor_set_section_unfold(section, true);
@@ -1084,6 +1169,8 @@ void EditorInspectorSection::fold() {
if (!foldable)
return;
+ if (!vbox_added)
+ return; //kinda pointless
#ifdef TOOLS_ENABLED
object->editor_set_section_unfold(section, false);
@@ -1105,7 +1192,14 @@ EditorInspectorSection::EditorInspectorSection() {
object = NULL;
foldable = false;
vbox = memnew(VBoxContainer);
- add_child(vbox);
+ vbox_added = false;
+ //add_child(vbox);
+}
+
+EditorInspectorSection::~EditorInspectorSection() {
+ if (!vbox_added) {
+ memdelete(vbox);
+ }
}
////////////////////////////////////////////////
@@ -1114,6 +1208,30 @@ EditorInspectorSection::EditorInspectorSection() {
Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS];
int EditorInspector::inspector_plugin_count = 0;
+EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ for (int i = inspector_plugin_count - 1; i >= 0; i--) {
+
+ inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage);
+ if (inspector_plugins[i]->added_editors.size()) {
+ for (int j = 1; j < inspector_plugins[i]->added_editors.size(); j++) { //only keep first one
+ memdelete(inspector_plugins[i]->added_editors[j].property_editor);
+ }
+
+ EditorProperty *prop = Object::cast_to<EditorProperty>(inspector_plugins[i]->added_editors[0].property_editor);
+ if (prop) {
+
+ inspector_plugins[i]->added_editors.clear();
+ return prop;
+ } else {
+ memdelete(inspector_plugins[i]->added_editors[0].property_editor);
+ inspector_plugins[i]->added_editors.clear();
+ }
+ }
+ }
+ return NULL;
+}
+
void EditorInspector::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS);
@@ -1242,8 +1360,10 @@ void EditorInspector::update_tree() {
String filter = search_box ? search_box->get_text() : "";
String group;
String group_base;
+ VBoxContainer *category_vbox = NULL;
- List<PropertyInfo> plist;
+ List<PropertyInfo>
+ plist;
object->get_property_list(&plist, true);
HashMap<String, VBoxContainer *> item_path;
@@ -1295,6 +1415,7 @@ void EditorInspector::update_tree() {
EditorInspectorCategory *category = memnew(EditorInspectorCategory);
main_vbox->add_child(category);
+ category_vbox = NULL; //reset
String type = p.name;
if (has_icon(type, "EditorIcons"))
@@ -1317,7 +1438,7 @@ void EditorInspector::update_tree() {
class_descr_cache[type] = descr.word_wrap(80);
}
- category->set_tooltip(TTR("Class:") + " " + p.name + (class_descr_cache[type] == "" ? "" : "\n\n" + class_descr_cache[type]));
+ category->set_tooltip(p.name + "::" + (class_descr_cache[type] == "" ? "" : class_descr_cache[type]));
}
for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
@@ -1380,6 +1501,11 @@ void EditorInspector::update_tree() {
continue;
}
+ if (category_vbox == NULL) {
+ category_vbox = memnew(VBoxContainer);
+ main_vbox->add_child(category_vbox);
+ }
+
VBoxContainer *current_vbox = main_vbox;
{
@@ -1407,6 +1533,14 @@ void EditorInspector::update_tree() {
current_vbox = item_path[acc_path];
level = (MIN(level + 1, 4));
}
+
+ if (current_vbox == main_vbox) {
+ //do not add directly to the main vbox, given it has no spacing
+ if (category_vbox == NULL) {
+ category_vbox = memnew(VBoxContainer);
+ }
+ current_vbox = category_vbox;
+ }
}
bool checkable = false;
@@ -1416,12 +1550,19 @@ void EditorInspector::update_tree() {
checked = p.usage & PROPERTY_USAGE_CHECKED;
}
+ if (p.usage & PROPERTY_USAGE_RESTART_IF_CHANGED) {
+ restart_request_props.insert(p.name);
+ }
+
String doc_hint;
if (use_doc_hints) {
StringName classname = object->get_class_name();
- StringName propname = p.name;
+ if (object_class != String()) {
+ classname = object_class;
+ }
+ StringName propname = property_prefix + p.name;
String descr;
bool found = false;
@@ -1464,7 +1605,8 @@ void EditorInspector::update_tree() {
#endif
for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
Ref<EditorInspectorPlugin> ped = E->get();
- ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage);
+ bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage);
+
List<EditorInspectorPlugin::AddedEditor> editors = ped->added_editors; //make a copy, since plugins may be used again in a sub-inspector
ped->added_editors.clear();
@@ -1488,9 +1630,9 @@ void EditorInspector::update_tree() {
ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED);
if (doc_hint != String()) {
- ep->set_tooltip(TTR("Property: ") + p.name + "\n\n" + doc_hint);
+ ep->set_tooltip(property_prefix + p.name + "::" + doc_hint);
} else {
- ep->set_tooltip(TTR("Property: ") + p.name);
+ ep->set_tooltip(property_prefix + p.name);
}
ep->set_draw_red(draw_red);
ep->set_use_folding(use_folding);
@@ -1532,6 +1674,10 @@ void EditorInspector::update_tree() {
}
}
}
+
+ if (exclusive) {
+ break;
+ }
}
}
@@ -1563,11 +1709,12 @@ void EditorInspector::_clear() {
editor_property_map.clear();
sections.clear();
pending.clear();
+ restart_request_props.clear();
}
void EditorInspector::refresh() {
- if (refresh_countdown > 0)
+ if (refresh_countdown > 0 || changing)
return;
refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval");
}
@@ -1588,6 +1735,10 @@ void EditorInspector::edit(Object *p_object) {
object = p_object;
if (object) {
+ update_scroll_request = 0; //reset
+ if (scroll_cache.has(object->get_instance_id())) { //if exists, set something else
+ update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accomodated
+ }
object->add_change_receptor(this);
update_tree();
}
@@ -1693,6 +1844,19 @@ int EditorInspector::get_scroll_offset() const {
return get_v_scroll();
}
+void EditorInspector::set_use_sub_inspector_bg(bool p_enable) {
+
+ use_sub_inspector_bg = p_enable;
+ if (!is_inside_tree())
+ return;
+
+ if (use_sub_inspector_bg) {
+ add_style_override("bg", get_stylebox("sub_inspector_bg", "Editor"));
+ } else {
+ add_style_override("bg", get_stylebox("bg", "Tree"));
+ }
+}
+
void EditorInspector::_edit_request_change(Object *p_object, const String &p_property) {
if (object != p_object) //may be undoing/redoing for a non edited object, so ignore
@@ -1768,9 +1932,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
undo_redo->add_do_method(this, "emit_signal", _prop_edited, p_name);
undo_redo->add_undo_method(this, "emit_signal", _prop_edited, p_name);
- changing++;
undo_redo->commit_action();
- changing--;
}
if (editor_property_map.has(p_name)) {
@@ -1780,9 +1942,21 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
}
-void EditorInspector::_property_changed(const String &p_path, const Variant &p_value) {
+void EditorInspector::_property_changed(const String &p_path, const Variant &p_value, bool changing) {
+
+ // The "changing" variable must be true for properties that trigger events as typing occurs,
+ // like "text_changed" signal. eg: Text property of Label, Button, RichTextLabel, etc.
+ if (changing)
+ this->changing++;
_edit_set(p_path, p_value, false, "");
+
+ if (changing)
+ this->changing--;
+
+ if (restart_request_props.has(p_path)) {
+ emit_signal("restart_requested");
+ }
}
void EditorInspector::_property_changed_update_all(const String &p_path, const Variant &p_value) {
@@ -1802,6 +1976,9 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array
undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS);
for (int i = 0; i < p_paths.size(); i++) {
_edit_set(p_paths[i], p_values[i], false, "");
+ if (restart_request_props.has(p_paths[i])) {
+ emit_signal("restart_requested");
+ }
}
changing++;
undo_redo->commit_action();
@@ -1874,6 +2051,8 @@ void EditorInspector::_property_selected(const String &p_path, int p_focusable)
E->get()->deselect();
}
}
+
+ emit_signal("property_selected", p_path);
}
void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) {
@@ -1897,7 +2076,11 @@ void EditorInspector::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
get_tree()->connect("node_removed", this, "_node_removed");
- add_style_override("bg", get_stylebox("bg", "Tree"));
+ if (use_sub_inspector_bg) {
+ add_style_override("bg", get_stylebox("sub_inspector_bg", "Editor"));
+ } else if (is_inside_tree()) {
+ add_style_override("bg", get_stylebox("bg", "Tree"));
+ }
}
if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -1907,6 +2090,10 @@ void EditorInspector::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
+ if (update_scroll_request >= 0) {
+ get_v_scrollbar()->call_deferred("set_value", update_scroll_request);
+ update_scroll_request = -1;
+ }
if (refresh_countdown > 0) {
refresh_countdown -= get_process_delta_time();
if (refresh_countdown <= 0) {
@@ -1954,10 +2141,36 @@ void EditorInspector::_changed_callback(Object *p_changed, const char *p_prop) {
_edit_request_change(p_changed, p_prop);
}
+void EditorInspector::_vscroll_changed(double p_offset) {
+
+ if (update_scroll_request >= 0) //waiting, do nothing
+ return;
+
+ if (object) {
+ scroll_cache[object->get_instance_id()] = p_offset;
+ }
+}
+
+void EditorInspector::set_property_prefix(const String &p_prefix) {
+ property_prefix = p_prefix;
+}
+
+String EditorInspector::get_property_prefix() const {
+ return property_prefix;
+}
+
+void EditorInspector::set_object_class(const String &p_class) {
+ object_class = p_class;
+}
+
+String EditorInspector::get_object_class() const {
+ return object_class;
+}
+
void EditorInspector::_bind_methods() {
+ ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(false));
ClassDB::bind_method("_multiple_properties_changed", &EditorInspector::_multiple_properties_changed);
- ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed);
ClassDB::bind_method("_property_changed_update_all", &EditorInspector::_property_changed_update_all);
ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
@@ -1969,10 +2182,16 @@ void EditorInspector::_bind_methods() {
ClassDB::bind_method("_property_selected", &EditorInspector::_property_selected);
ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected);
ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected);
+ ClassDB::bind_method("_vscroll_changed", &EditorInspector::_vscroll_changed);
+
+ ClassDB::bind_method("refresh", &EditorInspector::refresh);
+ ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("restart_requested"));
}
EditorInspector::EditorInspector() {
@@ -1980,6 +2199,7 @@ EditorInspector::EditorInspector() {
undo_redo = NULL;
main_vbox = memnew(VBoxContainer);
main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_vbox->add_constant_override("separation", 0);
add_child(main_vbox);
set_enable_h_scroll(false);
set_enable_v_scroll(true);
@@ -2001,4 +2221,8 @@ EditorInspector::EditorInspector() {
_prop_edited = "property_edited";
set_process(true);
property_focusable = -1;
+ use_sub_inspector_bg = false;
+
+ get_v_scrollbar()->connect("value_changed", this, "_vscroll_changed");
+ update_scroll_request = -1;
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 2a88be656a..454622d662 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -55,6 +55,9 @@ private:
bool draw_red;
bool keying;
+ Rect2 right_child_rect;
+ Rect2 bottom_child_rect;
+
Rect2 keying_rect;
bool keying_hover;
Rect2 revert_rect;
@@ -76,10 +79,14 @@ private:
bool selected;
int selected_focusable;
+ float split_ratio;
+
Vector<Control *> focusables;
Control *label_reference;
Control *bottom_editor;
+ mutable String tooltip_text;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -134,7 +141,14 @@ public:
void set_selectable(bool p_selectable);
bool is_selectable() const;
+ void set_name_split_ratio(float p_ratio);
+ float get_name_split_ratio() const;
+
void set_object_and_property(Object *p_object, const StringName &p_property);
+ virtual Control *make_custom_tooltip(const String &p_text) const;
+
+ String get_tooltip_text() const;
+
EditorProperty();
};
@@ -172,12 +186,17 @@ class EditorInspectorCategory : public Control {
Ref<Texture> icon;
String label;
Color bg_color;
+ mutable String tooltip_text;
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
virtual Size2 get_minimum_size() const;
+ virtual Control *make_custom_tooltip(const String &p_text) const;
+
+ String get_tooltip_text() const;
EditorInspectorCategory();
};
@@ -189,9 +208,12 @@ class EditorInspectorSection : public Container {
String section;
Object *object;
VBoxContainer *vbox;
+ bool vbox_added; //optimization
Color bg_color;
bool foldable;
+ void _test_unfold();
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -208,6 +230,7 @@ public:
Object *get_edited_object();
EditorInspectorSection();
+ ~EditorInspectorSection();
};
class EditorInspector : public ScrollContainer {
@@ -244,19 +267,27 @@ class EditorInspector : public ScrollContainer {
bool update_all_pending;
bool read_only;
bool keying;
+ bool use_sub_inspector_bg;
- int refresh_countdown;
+ float refresh_countdown;
bool update_tree_pending;
StringName _prop_edited;
StringName property_selected;
int property_focusable;
+ int update_scroll_request;
Map<StringName, Map<StringName, String> > descr_cache;
Map<StringName, String> class_descr_cache;
+ Set<StringName> restart_request_props;
+
+ Map<ObjectID, int> scroll_cache;
+
+ String property_prefix; //used for sectioned inspector
+ String object_class;
void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field);
- void _property_changed(const String &p_path, const Variant &p_value);
+ void _property_changed(const String &p_path, const Variant &p_value, bool changing = false);
void _property_changed_update_all(const String &p_path, const Variant &p_value);
void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
void _property_keyed(const String &p_path);
@@ -276,6 +307,8 @@ class EditorInspector : public ScrollContainer {
void _filter_changed(const String &p_text);
void _parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped);
+ void _vscroll_changed(double);
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -285,6 +318,8 @@ public:
static void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
static void cleanup_plugins();
+ static EditorProperty *instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+
void set_undo_redo(UndoRedo *p_undo_redo);
String get_selected_path() const;
@@ -323,6 +358,14 @@ public:
void set_scroll_offset(int p_offset);
int get_scroll_offset() const;
+ void set_property_prefix(const String &p_prefix);
+ String get_property_prefix() const;
+
+ void set_object_class(const String &p_class);
+ String get_object_class() const;
+
+ void set_use_sub_inspector_bg(bool p_enable);
+
EditorInspector();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 83f1cd40ba..ff97878e71 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -66,17 +66,21 @@
#include "editor/import/resource_importer_scene.h"
#include "editor/import/resource_importer_texture.h"
#include "editor/import/resource_importer_wav.h"
-#include "editor/plugins/animation_blend_space_editor.h"
+#include "editor/plugins/animation_blend_space_1d_editor.h"
+#include "editor/plugins/animation_blend_space_2d_editor.h"
#include "editor/plugins/animation_blend_tree_editor_plugin.h"
#include "editor/plugins/animation_player_editor_plugin.h"
+#include "editor/plugins/animation_state_machine_editor.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
+#include "editor/plugins/audio_stream_editor_plugin.h"
#include "editor/plugins/baked_lightmap_editor_plugin.h"
#include "editor/plugins/camera_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
#include "editor/plugins/collision_polygon_editor_plugin.h"
#include "editor/plugins/collision_shape_2d_editor_plugin.h"
+#include "editor/plugins/cpu_particles_editor_plugin.h"
#include "editor/plugins/cube_grid_theme_editor_plugin.h"
#include "editor/plugins/curve_editor_plugin.h"
#include "editor/plugins/editor_preview_plugins.h"
@@ -97,6 +101,7 @@
#include "editor/plugins/physical_bone_plugin.h"
#include "editor/plugins/polygon_2d_editor_plugin.h"
#include "editor/plugins/resource_preloader_editor_plugin.h"
+#include "editor/plugins/root_motion_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/plugins/script_text_editor.h"
#include "editor/plugins/shader_editor_plugin.h"
@@ -112,6 +117,7 @@
#include "editor/plugins/theme_editor_plugin.h"
#include "editor/plugins/tile_map_editor_plugin.h"
#include "editor/plugins/tile_set_editor_plugin.h"
+#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/pvrtc_compress.h"
#include "editor/register_exporters.h"
#include "editor/script_editor_debugger.h"
@@ -584,7 +590,6 @@ void EditorNode::edit_node(Node *p_node) {
void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) {
editor_data.apply_changes_in_editors();
-
int flg = 0;
if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources"))
flg |= ResourceSaver::FLAG_COMPRESS;
@@ -593,9 +598,7 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St
Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
if (err != OK) {
- current_option = -1;
- accept->set_text(TTR("Error saving resource!"));
- accept->popup_centered_minsize();
+ show_accept(TTR("Error saving resource!"), TTR("I see..."));
return;
}
@@ -681,26 +684,21 @@ void EditorNode::_dialog_display_save_error(String p_file, Error p_error) {
if (p_error) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
-
switch (p_error) {
case ERR_FILE_CANT_WRITE: {
- accept->set_text(TTR("Can't open file for writing:") + " " + p_file.get_extension());
+ show_accept(TTR("Can't open file for writing:") + " " + p_file.get_extension(), TTR("I see..."));
} break;
case ERR_FILE_UNRECOGNIZED: {
- accept->set_text(TTR("Requested file format unknown:") + " " + p_file.get_extension());
+ show_accept(TTR("Requested file format unknown:") + " " + p_file.get_extension(), TTR("I see..."));
} break;
default: {
- accept->set_text(TTR("Error while saving."));
+ show_accept(TTR("Error while saving."), TTR("I see..."));
} break;
}
-
- accept->popup_centered_minsize();
}
}
@@ -708,34 +706,29 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
if (p_error) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
-
switch (p_error) {
case ERR_CANT_OPEN: {
- accept->set_text(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()));
+ show_accept(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("I see..."));
} break;
case ERR_PARSE_ERROR: {
- accept->set_text(vformat(TTR("Error while parsing '%s'."), p_file.get_file()));
+ show_accept(vformat(TTR("Error while parsing '%s'."), p_file.get_file()), TTR("I see..."));
} break;
case ERR_FILE_CORRUPT: {
- accept->set_text(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()));
+ show_accept(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()), TTR("I see..."));
} break;
case ERR_FILE_NOT_FOUND: {
- accept->set_text(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()));
+ show_accept(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()), TTR("I see..."));
} break;
default: {
- accept->set_text(vformat(TTR("Error while loading '%s'."), p_file.get_file()));
+ show_accept(vformat(TTR("Error while loading '%s'."), p_file.get_file()), TTR("I see..."));
} break;
}
-
- accept->popup_centered_minsize();
}
}
@@ -996,10 +989,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
if (!scene) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This operation can't be done without a tree root."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a tree root."), TTR("I see..."));
return;
}
@@ -1027,10 +1017,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
if (err != OK) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."));
- accept->popup_centered_minsize();
+ show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("I see..."));
return;
}
@@ -1038,10 +1025,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
// (hacky but needed for the tree to update properly)
Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
if (!dummy_scene) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."));
- accept->popup_centered_minsize();
+ show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("I see..."));
return;
}
memdelete(dummy_scene);
@@ -1052,8 +1036,23 @@ void EditorNode::_save_scene(String p_file, int idx) {
flg |= ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
err = ResourceSaver::save(p_file, sdata, flg);
- Map<RES, bool> processed;
- _save_edited_subresources(scene, processed, flg);
+ //Map<RES, bool> processed;
+ //this method is slow and not always works, deprecating
+ //_save_edited_subresources(scene, processed, flg);
+ { //instead, just find globally unsaved subresources and save them
+
+ List<Ref<Resource> > cached;
+ ResourceCache::get_cached_resources(&cached);
+ for (List<Ref<Resource> >::Element *E = cached.front(); E; E = E->next()) {
+
+ Ref<Resource> res = E->get();
+ if (res->is_edited() && res->get_path().is_resource_file()) {
+ ResourceSaver::save(res->get_path(), res, flg);
+ res->set_edited(false);
+ }
+ }
+ }
+
editor_data.save_editor_external_data();
if (err == OK) {
scene->set_filename(ProjectSettings::get_singleton()->localize_path(p_file));
@@ -1069,10 +1068,35 @@ void EditorNode::_save_scene(String p_file, int idx) {
}
}
+void EditorNode::save_all_scenes_and_restart() {
+
+ _menu_option_confirm(RUN_STOP, true);
+ exiting = true;
+
+ _save_all_scenes();
+
+ String to_reopen;
+ if (get_tree()->get_edited_scene_root()) {
+ to_reopen = get_tree()->get_edited_scene_root()->get_filename();
+ }
+
+ get_tree()->quit();
+ String exec = OS::get_singleton()->get_executable_path();
+
+ List<String> args;
+ args.push_back("--path");
+ args.push_back(ProjectSettings::get_singleton()->get_resource_path());
+ args.push_back("-e");
+ if (to_reopen != String()) {
+ args.push_back(to_reopen);
+ }
+
+ OS::get_singleton()->set_restart_on_exit(true, args);
+}
+
void EditorNode::_save_all_scenes() {
- int i = _next_unsaved_scene(true, 0);
- while (i != -1) {
+ for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
Node *scene = editor_data.get_edited_scene_root(i);
if (scene && scene->get_filename() != "") {
if (i != editor_data.get_edited_scene())
@@ -1080,7 +1104,6 @@ void EditorNode::_save_all_scenes() {
else
_save_scene_with_preview(scene->get_filename());
} // else: ignore new scenes
- i = _next_unsaved_scene(true, ++i);
}
_save_default_environment();
@@ -1164,10 +1187,7 @@ void EditorNode::_dialog_action(String p_file) {
ml = ResourceLoader::load(p_file, "MeshLibrary");
if (ml.is_null()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Can't load MeshLibrary for merging!"));
- accept->popup_centered_minsize();
+ show_accept(TTR("Can't load MeshLibrary for merging!"), TTR("I see..."));
return;
}
}
@@ -1180,11 +1200,7 @@ void EditorNode::_dialog_action(String p_file) {
Error err = ResourceSaver::save(p_file, ml);
if (err) {
-
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Error saving MeshLibrary!"));
- accept->popup_centered_minsize();
+ show_accept(TTR("Error saving MeshLibrary!"), TTR("I see..."));
return;
}
@@ -1196,10 +1212,7 @@ void EditorNode::_dialog_action(String p_file) {
tileset = ResourceLoader::load(p_file, "TileSet");
if (tileset.is_null()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Can't load TileSet for merging!"));
- accept->popup_centered_minsize();
+ show_accept(TTR("Can't load TileSet for merging!"), TTR("I see..."));
return;
}
@@ -1212,10 +1225,7 @@ void EditorNode::_dialog_action(String p_file) {
Error err = ResourceSaver::save(p_file, tileset);
if (err) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Error saving TileSet!"));
- accept->popup_centered_minsize();
+ show_accept("Error saving TileSet!", "I see...");
return;
}
} break;
@@ -1356,8 +1366,7 @@ void EditorNode::_save_default_environment() {
if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
Map<RES, bool> processed;
_find_and_save_edited_subresources(fallback.ptr(), processed, 0);
- if (fallback->get_last_modified_time() != fallback->get_import_last_modified_time())
- save_resource_in_path(fallback, fallback->get_path());
+ save_resource_in_path(fallback, fallback->get_path());
}
}
@@ -1570,10 +1579,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("There is no defined scene to run."));
- accept->popup_centered_minsize();
+ show_accept(TTR("There is no defined scene to run."), TTR("I see..."));
return;
}
@@ -1627,10 +1633,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (scene->get_filename() == "") {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Current scene was never saved, please save it prior to running."));
- accept->popup_centered_minsize();
+ show_accept(TTR("Current scene was never saved, please save it prior to running."), TTR("I see..."));
return;
}
@@ -1661,10 +1664,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (error != OK) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("Could not start subprocess!"));
- accept->popup_centered_minsize();
+ show_accept(TTR("Could not start subprocess!"), TTR("I see..."));
return;
}
@@ -1782,10 +1782,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (!scene) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This operation can't be done without a tree root."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a tree root."), TTR("I see..."));
break;
}
@@ -1848,10 +1845,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (!editor_data.get_edited_scene_root()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This operation can't be done without a scene."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a scene."), TTR("I see..."));
break;
}
@@ -1871,10 +1865,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
//Make sure that the scene has a root before trying to convert to tileset
if (!editor_data.get_edited_scene_root()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This operation can't be done without a root node."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a root node."), TTR("I see..."));
break;
}
@@ -1891,18 +1882,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
- case SETTINGS_EXPORT_PREFERENCES: {
-
- //project_export_settings->popup_centered_ratio();
- } break;
case FILE_IMPORT_SUBSCENE: {
if (!editor_data.get_edited_scene_root()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This operation can't be done without a selected node."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This operation can't be done without a selected node."), TTR("I see..."));
break;
}
@@ -1932,25 +1916,29 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case EDIT_UNDO: {
if (Input::get_singleton()->get_mouse_button_mask() & 0x7) {
- break; // can't undo while mouse buttons are pressed
- }
-
- String action = editor_data.get_undo_redo().get_current_action_name();
- if (action != "")
- log->add_message("UNDO: " + action);
+ log->add_message("Can't UNDO while mouse buttons are pressed.");
+ } else {
+ String action = editor_data.get_undo_redo().get_current_action_name();
- editor_data.get_undo_redo().undo();
+ if (!editor_data.get_undo_redo().undo()) {
+ log->add_message("There is nothing to UNDO.");
+ } else if (action != "") {
+ log->add_message("UNDO: " + action);
+ }
+ }
} break;
case EDIT_REDO: {
- if (Input::get_singleton()->get_mouse_button_mask() & 0x7)
- break; // can't redo while mouse buttons are pressed
-
- editor_data.get_undo_redo().redo();
- String action = editor_data.get_undo_redo().get_current_action_name();
- if (action != "")
- log->add_message("REDO: " + action);
-
+ if (Input::get_singleton()->get_mouse_button_mask() & 0x7) {
+ log->add_message("Can't REDO while mouse buttons are pressed.");
+ } else {
+ if (!editor_data.get_undo_redo().redo()) {
+ log->add_message("There is nothing to REDO.");
+ } else {
+ String action = editor_data.get_undo_redo().get_current_action_name();
+ log->add_message("REDO: " + action);
+ }
+ }
} break;
case EDIT_REVERT: {
@@ -2171,10 +2159,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
OS::get_singleton()->set_low_processor_usage_mode(false);
EditorSettings::get_singleton()->set_project_metadata("editor_options", "update_always", true);
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see..."));
- accept->set_text(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."));
- accept->popup_centered_minsize();
+ show_accept(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."), TTR("I see..."));
} break;
case SETTINGS_UPDATE_CHANGES: {
@@ -2245,6 +2230,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
about->popup_centered_minsize(Size2(780, 500) * EDSCALE);
} break;
+ case SET_VIDEO_DRIVER_SAVE_AND_RESTART: {
+
+ ProjectSettings::get_singleton()->set("rendering/quality/driver/driver_name", video_driver_request);
+ ProjectSettings::get_singleton()->save();
+
+ save_all_scenes_and_restart();
+ } break;
default: {
if (p_option >= IMPORT_PLUGIN_BASE) {
}
@@ -2773,10 +2765,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
if (!lpath.begins_with("res://")) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("Ugh"));
- accept->set_text(TTR("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."));
- accept->popup_centered_minsize();
+ show_accept(TTR("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."), TTR("Ugh"));
opening_prev = false;
return ERR_FILE_NOT_FOUND;
}
@@ -3076,6 +3065,7 @@ void EditorNode::register_editor_types() {
ClassDB::register_class<EditorInspectorPlugin>();
ClassDB::register_class<EditorProperty>();
ClassDB::register_class<AnimationTrackEditPlugin>();
+ ClassDB::register_class<ScriptCreateDialog>();
// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
ClassDB::register_class<EditorScenePostImport>();
@@ -3190,6 +3180,13 @@ Error EditorNode::export_preset(const String &p_preset, const String &p_path, bo
return OK;
}
+void EditorNode::show_accept(const String &p_text, const String &p_title) {
+ current_option = -1;
+ accept->get_ok()->set_text(p_title);
+ accept->set_text(p_text);
+ accept->popup_centered_minsize();
+}
+
void EditorNode::show_warning(const String &p_text, const String &p_title) {
warning->set_text(p_text);
@@ -3894,7 +3891,7 @@ ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
tb->set_focus_mode(Control::FOCUS_NONE);
bottom_panel_vb->add_child(p_item);
bottom_panel_hb->raise();
- bottom_panel_hb->add_child(tb);
+ bottom_panel_hb_editors->add_child(tb);
p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
p_item->hide();
BottomPanelItem bpi;
@@ -3958,7 +3955,7 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) {
_bottom_panel_switch(false, 0);
}
bottom_panel_vb->remove_child(bottom_panel_items[i].control);
- bottom_panel_hb->remove_child(bottom_panel_items[i].button);
+ bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button);
memdelete(bottom_panel_items[i].button);
bottom_panel_items.remove(i);
break;
@@ -3988,6 +3985,11 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
center_split->set_collapsed(false);
+ if (bottom_panel_raise->is_pressed()) {
+ top_split->hide();
+ }
+ bottom_panel_raise->show();
+
} else {
bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
for (int i = 0; i < bottom_panel_items.size(); i++) {
@@ -3997,6 +3999,10 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
center_split->set_collapsed(true);
+ bottom_panel_raise->hide();
+ if (bottom_panel_raise->is_pressed()) {
+ top_split->show();
+ }
}
}
@@ -4213,7 +4219,7 @@ void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) {
for (int i = 0; i < p_files.size(); i++) {
String from = p_files[i];
- if (!ResourceFormatImporter::get_singleton()->can_be_imported(from) && (just_copy.find(from.get_extension().to_lower()) != -1)) {
+ if (!ResourceFormatImporter::get_singleton()->can_be_imported(from) && (just_copy.find(from.get_extension().to_lower()) == -1)) {
continue;
}
String to = to_path.plus_file(from.get_file());
@@ -4406,6 +4412,32 @@ Vector<Ref<EditorResourceConversionPlugin> > EditorNode::find_resource_conversio
return ret;
}
+void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
+
+ if (p_pressed) {
+ top_split->hide();
+ bottom_panel_raise->set_icon(gui_base->get_icon("ShrinkBottomDock", "EditorIcons"));
+ } else {
+ top_split->show();
+ bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
+ }
+}
+
+void EditorNode::_video_driver_selected(int p_which) {
+
+ String driver = video_driver->get_item_metadata(p_which);
+
+ String current = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
+
+ if (driver == current) {
+ return;
+ }
+
+ video_driver_request = driver;
+ video_restart_dialog->popup_centered_minsize();
+ video_driver->select(video_driver_current);
+}
+
void EditorNode::_bind_methods() {
ClassDB::bind_method("_menu_option", &EditorNode::_menu_option);
@@ -4431,6 +4463,8 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
+ ClassDB::bind_method("get_script_create_dialog", &EditorNode::get_script_create_dialog);
+
ClassDB::bind_method("_sources_changed", &EditorNode::_sources_changed);
ClassDB::bind_method("_fs_changed", &EditorNode::_fs_changed);
ClassDB::bind_method("_dock_select_draw", &EditorNode::_dock_select_draw);
@@ -4472,6 +4506,9 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_dim_timeout"), &EditorNode::_dim_timeout);
ClassDB::bind_method(D_METHOD("_resources_reimported"), &EditorNode::_resources_reimported);
+ ClassDB::bind_method(D_METHOD("_bottom_panel_raise_toggled"), &EditorNode::_bottom_panel_raise_toggled);
+
+ ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected);
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("pause_pressed"));
@@ -4633,6 +4670,14 @@ EditorNode::EditorNode() {
Ref<EditorInspectorDefaultPlugin> eidp;
eidp.instance();
EditorInspector::add_inspector_plugin(eidp);
+
+ Ref<EditorInspectorRootMotionPlugin> rmp;
+ rmp.instance();
+ EditorInspector::add_inspector_plugin(rmp);
+
+ Ref<EditorInspectorShaderModePlugin> smp;
+ smp.instance();
+ EditorInspector::add_inspector_plugin(smp);
}
_pvrtc_register_compressors();
@@ -4658,25 +4703,24 @@ EditorNode::EditorNode() {
GLOBAL_DEF("editor/main_run_args", "");
- ClassDB::set_class_enabled("CollisionShape", true);
- ClassDB::set_class_enabled("CollisionShape2D", true);
- ClassDB::set_class_enabled("CollisionPolygon2D", true);
+ ClassDB::set_class_enabled("RootMotionView", true);
//defs here, use EDITOR_GET in logic
- EDITOR_DEF("interface/scene_tabs/always_show_close_button", false);
- EDITOR_DEF("interface/scene_tabs/resize_if_many_tabs", true);
- EDITOR_DEF("interface/scene_tabs/minimum_width", 50);
+ EDITOR_DEF_RST("interface/scene_tabs/always_show_close_button", false);
+ EDITOR_DEF_RST("interface/scene_tabs/resize_if_many_tabs", true);
+ EDITOR_DEF_RST("interface/scene_tabs/minimum_width", 50);
EDITOR_DEF("run/output/always_clear_output_on_play", true);
EDITOR_DEF("run/output/always_open_output_on_play", true);
EDITOR_DEF("run/output/always_close_output_on_stop", true);
EDITOR_DEF("run/auto_save/save_before_running", true);
- EDITOR_DEF("interface/editor/save_each_scene_on_quit", true);
+ EDITOR_DEF_RST("interface/editor/save_each_scene_on_quit", true);
EDITOR_DEF("interface/editor/quit_confirmation", true);
- EDITOR_DEF("interface/scene_tabs/restore_scenes_on_load", false);
- EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true);
- EDITOR_DEF("interface/inspector/capitalize_properties", true);
- EDITOR_DEF("interface/inspector/disable_folding", false);
- EDITOR_DEF("interface/inspector/open_resources_in_new_inspector", false);
+ EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false);
+ EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
+ EDITOR_DEF_RST("interface/inspector/capitalize_properties", true);
+ EDITOR_DEF_RST("interface/inspector/disable_folding", false);
+ EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
+ EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial");
EDITOR_DEF("run/auto_save/save_before_running", true);
theme_base = memnew(Control);
@@ -5197,6 +5241,37 @@ EditorNode::EditorNode() {
play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5));
#endif
+ video_driver = memnew(OptionButton);
+ video_driver->set_flat(true);
+ video_driver->set_focus_mode(Control::FOCUS_NONE);
+ video_driver->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string;
+ String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
+ menu_hb->add_child(video_driver);
+ video_driver_current = 0;
+ for (int i = 0; i < video_drivers.get_slice_count(","); i++) {
+ String driver = video_drivers.get_slice(",", i);
+ if (gui_base->has_icon(driver, "EditorIcons")) {
+ video_driver->add_icon_item(gui_base->get_icon(driver, "EditorIcons"), "");
+ } else {
+ video_driver->add_item(driver);
+ }
+
+ video_driver->set_item_metadata(i, driver);
+
+ if (current_video_driver == driver) {
+ video_driver->select(i);
+ video_driver_current = i;
+ }
+ }
+
+ video_driver->connect("item_selected", this, "_video_driver_selected");
+ video_restart_dialog = memnew(ConfirmationDialog);
+ video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor."));
+ video_restart_dialog->get_ok()->set_text(TTR("Save & Restart"));
+ video_restart_dialog->connect("confirmed", this, "_menu_option", varray(SET_VIDEO_DRIVER_SAVE_AND_RESTART));
+ gui_base->add_child(video_restart_dialog);
+
progress_hb = memnew(BackgroundProgress);
HBoxContainer *right_menu_hb = memnew(HBoxContainer);
@@ -5214,8 +5289,8 @@ EditorNode::EditorNode() {
update_menu->set_icon(gui_base->get_icon("Progress1", "EditorIcons"));
update_menu->get_popup()->connect("id_pressed", this, "_menu_option");
p = update_menu->get_popup();
- p->add_check_item(TTR("Update Always"), SETTINGS_UPDATE_ALWAYS);
- p->add_check_item(TTR("Update Changes"), SETTINGS_UPDATE_CHANGES);
+ p->add_radio_check_item(TTR("Update Always"), SETTINGS_UPDATE_ALWAYS);
+ p->add_radio_check_item(TTR("Update Changes"), SETTINGS_UPDATE_CHANGES);
p->add_separator();
p->add_check_item(TTR("Disable Update Spinner"), SETTINGS_UPDATE_SPINNER_HIDE);
int update_always = EditorSettings::get_singleton()->get_project_metadata("editor_options", "update_always", false);
@@ -5293,6 +5368,19 @@ EditorNode::EditorNode() {
bottom_panel_hb = memnew(HBoxContainer);
bottom_panel_vb->add_child(bottom_panel_hb);
+ bottom_panel_hb_editors = memnew(HBoxContainer);
+ bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bottom_panel_hb->add_child(bottom_panel_hb_editors);
+ bottom_panel_raise = memnew(ToolButton);
+ bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
+
+ bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12));
+
+ bottom_panel_hb->add_child(bottom_panel_raise);
+ bottom_panel_raise->hide();
+ bottom_panel_raise->set_toggle_mode(true);
+ bottom_panel_raise->connect("toggled", this, "_bottom_panel_raise_toggled");
+
log = memnew(EditorLog);
ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log);
log->set_tool_button(output_button);
@@ -5397,10 +5485,11 @@ EditorNode::EditorNode() {
raise_bottom_panel_item(AnimationPlayerEditor::singleton);
add_editor_plugin(memnew(ShaderEditorPlugin(this)));
- // FIXME: Disabled for Godot 3.0 as made incompatible, it needs to be ported to the new API.
- //add_editor_plugin(memnew(ShaderGraphEditorPlugin(this)));
+ add_editor_plugin(memnew(VisualShaderEditorPlugin(this)));
add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this)));
- add_editor_plugin(memnew(AnimationNodeBlendSpaceEditorPlugin(this)));
+ add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this)));
+ add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this)));
+ add_editor_plugin(memnew(AnimationNodeStateMachineEditorPlugin(this)));
add_editor_plugin(memnew(CameraEditorPlugin(this)));
add_editor_plugin(memnew(ThemeEditorPlugin(this)));
@@ -5412,6 +5501,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(SpriteEditorPlugin(this)));
add_editor_plugin(memnew(Skeleton2DEditorPlugin(this)));
add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
+ add_editor_plugin(memnew(CPUParticlesEditorPlugin(this)));
add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
@@ -5433,6 +5523,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this)));
+ add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index dedd947633..38e68b2e09 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -164,7 +164,6 @@ private:
SETTINGS_UPDATE_ALWAYS,
SETTINGS_UPDATE_CHANGES,
SETTINGS_UPDATE_SPINNER_HIDE,
- SETTINGS_EXPORT_PREFERENCES,
SETTINGS_PREFERENCES,
SETTINGS_LAYOUT_SAVE,
SETTINGS_LAYOUT_DELETE,
@@ -183,6 +182,8 @@ private:
HELP_COMMUNITY,
HELP_ABOUT,
+ SET_VIDEO_DRIVER_SAVE_AND_RESTART,
+
IMPORT_PLUGIN_BASE = 100,
TOOL_MENU_BASE = 1000
@@ -195,6 +196,13 @@ private:
Control *gui_base;
VBoxContainer *main_vbox;
PanelContainer *play_button_panel;
+ OptionButton *video_driver;
+
+ ConfirmationDialog *video_restart_dialog;
+
+ int video_driver_current;
+ String video_driver_request;
+ void _video_driver_selected(int);
//split
@@ -377,7 +385,11 @@ private:
PanelContainer *bottom_panel;
HBoxContainer *bottom_panel_hb;
+ HBoxContainer *bottom_panel_hb_editors;
VBoxContainer *bottom_panel_vb;
+ ToolButton *bottom_panel_raise;
+
+ void _bottom_panel_raise_toggled(bool);
EditorInterface *editor_interface;
@@ -598,6 +610,7 @@ public:
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
EditorInspector *get_inspector() { return inspector_dock->get_inspector(); }
Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); }
+ ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); }
ProjectSettingsEditor *get_project_settings() { return project_settings; }
@@ -685,6 +698,7 @@ public:
Ref<Theme> get_editor_theme() const { return theme; }
+ void show_accept(const String &p_text, const String &p_title);
void show_warning(const String &p_text, const String &p_title = "Warning!");
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, const String &p_password, bool p_quit_after = false);
@@ -740,6 +754,8 @@ public:
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
+ void save_all_scenes_and_restart();
+
void dim_editor(bool p_dimming);
void edit_current() { _edit_current(); };
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index cc44938c25..843267d673 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -310,7 +310,7 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) {
}
ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
-
+ ERR_FAIL_NULL_V(p_control, NULL);
return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control);
}
@@ -333,6 +333,7 @@ void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
}
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
+ ERR_FAIL_NULL(p_control);
switch (p_location) {
@@ -382,6 +383,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
}
void EditorPlugin::remove_control_from_container(CustomControlContainer p_location, Control *p_control) {
+ ERR_FAIL_NULL(p_control);
switch (p_location) {
@@ -717,6 +719,10 @@ EditorInterface *EditorPlugin::get_editor_interface() {
return EditorInterface::get_singleton();
}
+ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
+ return EditorNode::get_singleton()->get_script_create_dialog();
+}
+
void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
@@ -753,6 +759,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
+ ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index fcc74cb1e9..72e21b2f7f 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -34,6 +34,7 @@
#include "editor/editor_inspector.h"
#include "editor/import/editor_import_plugin.h"
#include "editor/import/resource_importer_scene.h"
+#include "editor/script_create_dialog.h"
#include "io/config_file.h"
#include "scene/gui/tool_button.h"
#include "scene/main/node.h"
@@ -195,6 +196,7 @@ public:
virtual bool build(); // builds with external tools. Returns true if safe to continue running scene.
EditorInterface *get_editor_interface();
+ ScriptCreateDialog *get_script_create_dialog();
int update_overlays() const;
diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp
index fcd3dc2f68..d4a97b7095 100644
--- a/editor/editor_profiler.cpp
+++ b/editor/editor_profiler.cpp
@@ -424,20 +424,25 @@ void EditorProfiler::_update_frame() {
void EditorProfiler::_activate_pressed() {
if (activate->is_pressed()) {
- clear();
activate->set_icon(get_icon("Stop", "EditorIcons"));
- activate->set_text(TTR("Stop Profiling"));
+ activate->set_text(TTR("Stop"));
} else {
activate->set_icon(get_icon("Play", "EditorIcons"));
- activate->set_text(TTR("Start Profiling"));
+ activate->set_text(TTR("Start"));
}
emit_signal("enable_profiling", activate->is_pressed());
}
+void EditorProfiler::_clear_pressed() {
+
+ clear();
+}
+
void EditorProfiler::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
activate->set_icon(get_icon("Play", "EditorIcons"));
+ clear_button->set_icon(get_icon("Clear", "EditorIcons"));
}
}
@@ -599,6 +604,7 @@ void EditorProfiler::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_frame"), &EditorProfiler::_update_frame);
ClassDB::bind_method(D_METHOD("_update_plot"), &EditorProfiler::_update_plot);
ClassDB::bind_method(D_METHOD("_activate_pressed"), &EditorProfiler::_activate_pressed);
+ ClassDB::bind_method(D_METHOD("_clear_pressed"), &EditorProfiler::_clear_pressed);
ClassDB::bind_method(D_METHOD("_graph_tex_draw"), &EditorProfiler::_graph_tex_draw);
ClassDB::bind_method(D_METHOD("_graph_tex_input"), &EditorProfiler::_graph_tex_input);
ClassDB::bind_method(D_METHOD("_graph_tex_mouse_exit"), &EditorProfiler::_graph_tex_mouse_exit);
@@ -625,10 +631,15 @@ EditorProfiler::EditorProfiler() {
add_child(hb);
activate = memnew(Button);
activate->set_toggle_mode(true);
- activate->set_text(TTR("Start Profiling"));
+ activate->set_text(TTR("Start"));
activate->connect("pressed", this, "_activate_pressed");
hb->add_child(activate);
+ clear_button = memnew(Button);
+ clear_button->set_text(TTR("Clear"));
+ clear_button->connect("pressed", this, "_clear_pressed");
+ hb->add_child(clear_button);
+
hb->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
diff --git a/editor/editor_profiler.h b/editor/editor_profiler.h
index d902a97c5d..cb451475e7 100644
--- a/editor/editor_profiler.h
+++ b/editor/editor_profiler.h
@@ -100,6 +100,7 @@ public:
private:
Button *activate;
+ Button *clear_button;
TextureRect *graph;
Ref<ImageTexture> graph_texture;
PoolVector<uint8_t> graph_image;
@@ -133,6 +134,7 @@ private:
void _update_frame();
void _activate_pressed();
+ void _clear_pressed();
String _get_time_as_text(Metric &m, float p_time, int p_calls);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 5abcae80e0..0b49b5a801 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -50,7 +50,7 @@ void EditorPropertyText::_text_changed(const String &p_string) {
if (updating)
return;
- emit_signal("property_changed", get_edited_property(), p_string);
+ emit_signal("property_changed", get_edited_property(), p_string, true);
}
void EditorPropertyText::update_property() {
@@ -78,12 +78,12 @@ EditorPropertyText::EditorPropertyText() {
void EditorPropertyMultilineText::_big_text_changed() {
text->set_text(big_text->get_text());
- emit_signal("property_changed", get_edited_property(), big_text->get_text());
+ emit_signal("property_changed", get_edited_property(), big_text->get_text(), true);
}
void EditorPropertyMultilineText::_text_changed() {
- emit_signal("property_changed", get_edited_property(), text->get_text());
+ emit_signal("property_changed", get_edited_property(), text->get_text(), true);
}
void EditorPropertyMultilineText::_open_big_text() {
@@ -178,6 +178,8 @@ void EditorPropertyTextEnum::_bind_methods() {
EditorPropertyTextEnum::EditorPropertyTextEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
+ options->set_flat(true);
+
add_child(options);
add_focusable(options);
options->connect("item_selected", this, "_option_selected");
@@ -390,13 +392,37 @@ EditorPropertyCheck::EditorPropertyCheck() {
void EditorPropertyEnum::_option_selected(int p_which) {
- emit_signal("property_changed", get_edited_property(), p_which);
+ String text = options->get_item_text(p_which);
+ Vector<String> text_split = text.split(":");
+ if (text_split.size() == 1) {
+ emit_signal("property_changed", get_edited_property(), p_which);
+ return;
+ }
+ String name = text_split[1];
+ emit_signal("property_changed", get_edited_property(), name.to_int());
}
void EditorPropertyEnum::update_property() {
int which = get_edited_object()->get(get_edited_property());
- options->select(which);
+ if (which == 0) {
+ options->select(which);
+ return;
+ }
+
+ for (int i = 0; i < options->get_item_count(); i++) {
+ String text = options->get_item_text(i);
+ Vector<String> text_split = text.split(":");
+ if (text_split.size() == 1) {
+ options->select(which);
+ return;
+ }
+ String name = text_split[1];
+ if (itos(which) == name) {
+ options->select(i);
+ return;
+ }
+ }
}
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
@@ -405,6 +431,10 @@ void EditorPropertyEnum::setup(const Vector<String> &p_options) {
}
}
+void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
+ options->set_clip_text(p_enable);
+}
+
void EditorPropertyEnum::_bind_methods() {
ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyEnum::_option_selected);
@@ -413,6 +443,7 @@ void EditorPropertyEnum::_bind_methods() {
EditorPropertyEnum::EditorPropertyEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
+ options->set_flat(true);
add_child(options);
add_focusable(options);
options->connect("item_selected", this, "_option_selected");
@@ -613,9 +644,11 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
}
if (name == "") {
- name = "Layer " + itos(i + 1);
+ name = TTR("Layer") + " " + itos(i + 1);
}
+ name += "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i);
+
names.push_back(name);
}
@@ -705,6 +738,7 @@ void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bo
EditorPropertyInteger::EditorPropertyInteger() {
spin = memnew(EditorSpinSlider);
+ spin->set_flat(true);
add_child(spin);
add_focusable(spin);
spin->connect("value_changed", this, "_value_changed");
@@ -791,6 +825,7 @@ void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool
EditorPropertyFloat::EditorPropertyFloat() {
spin = memnew(EditorSpinSlider);
+ spin->set_flat(true);
add_child(spin);
add_focusable(spin);
spin->connect("value_changed", this, "_value_changed");
@@ -801,6 +836,12 @@ EditorPropertyFloat::EditorPropertyFloat() {
void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
+ Ref<InputEventMouseButton> mb = p_ev;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ preset->set_global_position(easing_draw->get_global_transform().xform(mb->get_position()));
+ preset->popup();
+ }
+
Ref<InputEventMouseMotion> mm = p_ev;
if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
@@ -838,7 +879,7 @@ void EditorPropertyEasing::_draw_easing() {
Size2 s = easing_draw->get_size();
Rect2 r(Point2(), s);
r = r.grow(3);
- get_stylebox("normal", "LineEdit")->draw(ci, r);
+ //get_stylebox("normal", "LineEdit")->draw(ci, r);
int points = 48;
@@ -848,6 +889,7 @@ void EditorPropertyEasing::_draw_easing() {
Ref<Font> f = get_font("font", "Label");
Color color = get_color("font_color", "Label");
+ Vector<Point2> lines;
for (int i = 1; i <= points; i++) {
float ifl = i / float(points);
@@ -860,10 +902,12 @@ void EditorPropertyEasing::_draw_easing() {
iflp = 1.0 - iflp;
}
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(iflp * s.width, prev * s.height), Point2(ifl * s.width, h * s.height), color);
+ lines.push_back(Point2(ifl * s.width, h * s.height));
+ lines.push_back(Point2(iflp * s.width, prev * s.height));
prev = h;
}
+ easing_draw->draw_multiline(lines, color, 1.0, true);
f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color);
}
@@ -871,29 +915,17 @@ void EditorPropertyEasing::update_property() {
easing_draw->update();
}
-void EditorPropertyEasing::_set_preset(float p_val) {
- emit_signal("property_changed", get_edited_property(), p_val);
+void EditorPropertyEasing::_set_preset(int p_preset) {
+ static const float preset_value[EASING_MAX] = { 0.0, 1.0, 2.0, 0.5, -2.0, -0.5 };
+
+ emit_signal("property_changed", get_edited_property(), preset_value[p_preset]);
easing_draw->update();
}
void EditorPropertyEasing::setup(bool p_full, bool p_flip) {
flip = p_flip;
- if (p_full) {
- HBoxContainer *hb2 = memnew(HBoxContainer);
- vb->add_child(hb2);
- button_out_in = memnew(ToolButton);
- button_out_in->set_tooltip(TTR("Out-In"));
- button_out_in->set_h_size_flags(SIZE_EXPAND_FILL);
- button_out_in->connect("pressed", this, "_set_preset", varray(-0.5));
- hb2->add_child(button_out_in);
-
- button_in_out = memnew(ToolButton);
- button_in_out->set_tooltip(TTR("In"));
- button_in_out->set_h_size_flags(SIZE_EXPAND_FILL);
- button_in_out->connect("pressed", this, "_set_preset", varray(-2));
- hb2->add_child(button_in_out);
- }
+ full = p_full;
}
void EditorPropertyEasing::_notification(int p_what) {
@@ -901,15 +933,19 @@ void EditorPropertyEasing::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
+ preset->clear();
+ preset->add_icon_item(get_icon("CurveConstant", "EditorIcons"), "Zero", EASING_ZERO);
+ preset->add_icon_item(get_icon("CurveLinear", "EditorIcons"), "Linear", EASING_LINEAR);
+ preset->add_icon_item(get_icon("CurveIn", "EditorIcons"), "In", EASING_IN);
+ preset->add_icon_item(get_icon("CurveOut", "EditorIcons"), "Out", EASING_OUT);
+ if (full) {
+ preset->add_icon_item(get_icon("CurveInOut", "EditorIcons"), "In-Out", EASING_IN_OUT);
+ preset->add_icon_item(get_icon("CurveOutIn", "EditorIcons"), "Out-In", EASING_OUT_IN);
+ }
easing_draw->set_custom_minimum_size(Size2(0, get_font("font", "Label")->get_height() * 2));
- button_linear->set_icon(get_icon("CurveLinear", "EditorIcons"));
- button_out->set_icon(get_icon("CurveOut", "EditorIcons"));
- button_in->set_icon(get_icon("CurveIn", "EditorIcons"));
- button_constant->set_icon(get_icon("CurveConstant", "EditorIcons"));
- if (button_out_in)
- button_out_in->set_icon(get_icon("CurveOutIn", "EditorIcons"));
- if (button_in_out)
- button_in_out->set_icon(get_icon("CurveInOut", "EditorIcons"));
+ } break;
+ case NOTIFICATION_RESIZED: {
+
} break;
}
}
@@ -923,47 +959,18 @@ void EditorPropertyEasing::_bind_methods() {
EditorPropertyEasing::EditorPropertyEasing() {
- vb = memnew(VBoxContainer);
- add_child(vb);
- HBoxContainer *hb = memnew(HBoxContainer);
- set_label_reference(hb);
-
- vb->add_child(hb);
-
- button_linear = memnew(ToolButton);
- button_linear->set_tooltip(TTR("Linear"));
- button_linear->set_h_size_flags(SIZE_EXPAND_FILL);
- button_linear->connect("pressed", this, "_set_preset", varray(1));
- hb->add_child(button_linear);
-
- button_constant = memnew(ToolButton);
- button_constant->set_tooltip(TTR("Linear"));
- button_constant->set_h_size_flags(SIZE_EXPAND_FILL);
- button_constant->connect("pressed", this, "_set_preset", varray(0));
- hb->add_child(button_constant);
-
- button_out = memnew(ToolButton);
- button_out->set_tooltip(TTR("Out"));
- button_out->set_h_size_flags(SIZE_EXPAND_FILL);
- button_out->connect("pressed", this, "_set_preset", varray(0.5));
- hb->add_child(button_out);
-
- button_in = memnew(ToolButton);
- button_in->set_tooltip(TTR("In"));
- button_in->set_h_size_flags(SIZE_EXPAND_FILL);
- button_in->connect("pressed", this, "_set_preset", varray(2));
- hb->add_child(button_in);
-
- button_in_out = NULL;
- button_out_in = NULL;
-
easing_draw = memnew(Control);
easing_draw->connect("draw", this, "_draw_easing");
easing_draw->connect("gui_input", this, "_drag_easing");
easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);
- vb->add_child(easing_draw);
+ add_child(easing_draw);
+
+ preset = memnew(PopupMenu);
+ add_child(preset);
+ preset->connect("id_pressed", this, "_set_preset");
flip = false;
+ full = false;
}
///////////////////// VECTOR2 /////////////////////////
@@ -986,6 +993,18 @@ void EditorPropertyVector2::update_property() {
setting = false;
}
+void EditorPropertyVector2::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 2; i++) {
+
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
+
void EditorPropertyVector2::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed);
@@ -1006,6 +1025,7 @@ EditorPropertyVector2::EditorPropertyVector2() {
static const char *desc[2] = { "x", "y" };
for (int i = 0; i < 2; i++) {
spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_flat(true);
spin[i]->set_label(desc[i]);
vb->add_child(spin[i]);
add_focusable(spin[i]);
@@ -1038,7 +1058,17 @@ void EditorPropertyRect2::update_property() {
spin[3]->set_value(val.size.y);
setting = false;
}
+void EditorPropertyRect2::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 4; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 2) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyRect2::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyRect2::_value_changed);
@@ -1060,6 +1090,8 @@ EditorPropertyRect2::EditorPropertyRect2() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1088,7 +1120,17 @@ void EditorPropertyVector3::update_property() {
spin[2]->set_value(val.z);
setting = false;
}
+void EditorPropertyVector3::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyVector3::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed);
@@ -1110,6 +1152,8 @@ EditorPropertyVector3::EditorPropertyVector3() {
for (int i = 0; i < 3; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1140,7 +1184,17 @@ void EditorPropertyPlane::update_property() {
spin[3]->set_value(val.d);
setting = false;
}
+void EditorPropertyPlane::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyPlane::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyPlane::_value_changed);
@@ -1162,6 +1216,7 @@ EditorPropertyPlane::EditorPropertyPlane() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1193,7 +1248,17 @@ void EditorPropertyQuat::update_property() {
spin[3]->set_value(val.w);
setting = false;
}
+void EditorPropertyQuat::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyQuat::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyQuat::_value_changed);
@@ -1215,6 +1280,8 @@ EditorPropertyQuat::EditorPropertyQuat() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1252,7 +1319,17 @@ void EditorPropertyAABB::update_property() {
setting = false;
}
+void EditorPropertyAABB::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 6; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyAABB::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyAABB::_value_changed);
@@ -1276,6 +1353,8 @@ EditorPropertyAABB::EditorPropertyAABB() {
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1314,7 +1393,17 @@ void EditorPropertyTransform2D::update_property() {
setting = false;
}
+void EditorPropertyTransform2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 6; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 2) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyTransform2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform2D::_value_changed);
@@ -1334,10 +1423,11 @@ EditorPropertyTransform2D::EditorPropertyTransform2D() {
g->set_columns(2);
add_child(g);
- static const char *desc[6] = { "xx", "xy", "yx", "yy", "ox", "oy" };
+ static const char *desc[6] = { "x", "y", "x", "y", "x", "y" };
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1382,7 +1472,17 @@ void EditorPropertyBasis::update_property() {
setting = false;
}
+void EditorPropertyBasis::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 9; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyBasis::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyBasis::_value_changed);
@@ -1402,10 +1502,11 @@ EditorPropertyBasis::EditorPropertyBasis() {
g->set_columns(3);
add_child(g);
- static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };
+ static const char *desc[9] = { "x", "y", "z", "x", "y", "z", "x", "y", "z" };
for (int i = 0; i < 9; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1456,7 +1557,17 @@ void EditorPropertyTransform::update_property() {
setting = false;
}
+void EditorPropertyTransform::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 12; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform::_value_changed);
@@ -1476,10 +1587,11 @@ EditorPropertyTransform::EditorPropertyTransform() {
g->set_columns(3);
add_child(g);
- static const char *desc[12] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz" };
+ static const char *desc[12] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" };
for (int i = 0; i < 12; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1522,14 +1634,23 @@ EditorPropertyColor::EditorPropertyColor() {
void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
+ NodePath path = p_path;
Node *base_node = Object::cast_to<Node>(get_edited_object());
- emit_signal("property_changed", get_edited_property(), base_node->get_path().rel_path_to(p_path));
+ if (base_node == NULL && get_edited_object()->has_method("get_root_path")) {
+ base_node = get_edited_object()->call("get_root_path");
+ }
+ if (base_node) { // for AnimationTrackKeyEdit
+ path = base_node->get_path().rel_path_to(p_path);
+ }
+ emit_signal("property_changed", get_edited_property(), path);
update_property();
}
void EditorPropertyNodePath::_node_assign() {
if (!scene_tree) {
scene_tree = memnew(SceneTreeDialog);
+ scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
+ scene_tree->get_scene_tree()->set_valid_types(valid_types);
add_child(scene_tree);
scene_tree->connect("selected", this, "_node_selected");
}
@@ -1584,9 +1705,10 @@ void EditorPropertyNodePath::update_property() {
assign->set_icon(icon);
}
-void EditorPropertyNodePath::setup(const NodePath &p_base_hint) {
+void EditorPropertyNodePath::setup(const NodePath &p_base_hint, Vector<StringName> p_valid_types) {
base_hint = p_base_hint;
+ valid_types = p_valid_types;
}
void EditorPropertyNodePath::_notification(int p_what) {
@@ -1779,6 +1901,7 @@ void EditorPropertyResource::_menu_option(int p_which) {
if (!scene_tree) {
scene_tree = memnew(SceneTreeDialog);
+ scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
add_child(scene_tree);
scene_tree->connect("selected", this, "_viewport_selected");
scene_tree->set_title(TTR("Pick a Viewport"));
@@ -1815,7 +1938,19 @@ void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<T
RES p = get_edited_object()->get(get_edited_property());
if (p.is_valid() && p->get_instance_id() == p_obj) {
if (p_preview.is_valid()) {
- assign->set_icon(p_preview);
+ String type = p->get_class_name();
+ preview->set_margin(MARGIN_LEFT, assign->get_icon()->get_width() + assign->get_stylebox("normal")->get_default_margin(MARGIN_LEFT) + get_constant("hseparation", "Button"));
+ if (type == "GradientTexture") {
+ preview->set_stretch_mode(TextureRect::STRETCH_SCALE);
+ assign->set_custom_minimum_size(Size2(1, 1));
+ } else {
+ preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+ assign->set_custom_minimum_size(Size2(1, thumbnail_size));
+ }
+ preview->set_texture(p_preview);
+ assign->set_text("");
}
}
}
@@ -1989,7 +2124,7 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
void EditorPropertyResource::_open_editor_pressed() {
RES res = get_edited_object()->get(get_edited_property());
if (res.is_valid()) {
- EditorNode::get_singleton()->edit_item(res.ptr());
+ EditorNode::get_singleton()->edit_resource(res.ptr());
}
}
@@ -2009,6 +2144,9 @@ void EditorPropertyResource::update_property() {
sub_inspector = memnew(EditorInspector);
sub_inspector->set_enable_v_scroll(false);
+ sub_inspector->set_use_sub_inspector_bg(true);
+ sub_inspector->set_enable_capitalize_paths(true);
+
sub_inspector->connect("property_keyed", this, "_sub_inspector_property_keyed");
sub_inspector->connect("resource_selected", this, "_sub_inspector_resource_selected");
sub_inspector->connect("object_id_selected", this, "_sub_inspector_object_id_selected");
@@ -2056,6 +2194,7 @@ void EditorPropertyResource::update_property() {
#endif
}
+ preview->set_texture(Ref<Texture>());
if (res == RES()) {
assign->set_icon(Ref<Texture>());
assign->set_text(TTR("[empty]"));
@@ -2255,6 +2394,10 @@ void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant &
}
}
+void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {
+ use_sub_inspector = p_enable;
+}
+
void EditorPropertyResource::_bind_methods() {
ClassDB::bind_method(D_METHOD("_file_selected"), &EditorPropertyResource::_file_selected);
@@ -2277,7 +2420,8 @@ EditorPropertyResource::EditorPropertyResource() {
sub_inspector = NULL;
sub_inspector_vbox = NULL;
- use_sub_inspector = !bool(EDITOR_GET("interface/inspector/open_resources_in_new_inspector"));
+ use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
+
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
assign = memnew(Button);
@@ -2289,6 +2433,14 @@ EditorPropertyResource::EditorPropertyResource() {
assign->connect("draw", this, "_button_draw");
hbc->add_child(assign);
+ preview = memnew(TextureRect);
+ preview->set_expand(true);
+ preview->set_anchors_and_margins_preset(PRESET_WIDE);
+ preview->set_margin(MARGIN_TOP, 1);
+ preview->set_margin(MARGIN_BOTTOM, -1);
+ preview->set_margin(MARGIN_RIGHT, -1);
+ assign->add_child(preview);
+
menu = memnew(PopupMenu);
add_child(menu);
edit = memnew(Button);
@@ -2665,7 +2817,12 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);
if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) {
- editor->setup(p_hint_text);
+ editor->setup(p_hint_text, Vector<StringName>());
+ }
+ if (p_hint == PROPERTY_HINT_NODE_PATH_VALID_TYPES && p_hint_text != String()) {
+ Vector<String> types = p_hint_text.split(",", false);
+ Vector<StringName> sn = Variant(types); //convert via variant
+ editor->setup(NodePath(), sn);
}
add_property_editor(p_path, editor);
@@ -2675,6 +2832,22 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case Variant::OBJECT: {
EditorPropertyResource *editor = memnew(EditorPropertyResource);
editor->setup(p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
+
+ if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ String open_in_new = EDITOR_GET("interface/inspector/resources_types_to_open_in_new_inspector");
+ for (int i = 0; i < open_in_new.get_slice_count(","); i++) {
+ String type = open_in_new.get_slicec(',', i).strip_edges();
+ for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {
+ String inherits = p_hint_text.get_slicec(',', j);
+
+ if (ClassDB::is_parent_class(inherits, type)) {
+
+ editor->set_use_sub_inspector(false);
+ }
+ }
+ }
+ }
+
add_property_editor(p_path, editor);
} break;
@@ -2684,34 +2857,42 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::ARRAY);
add_property_editor(p_path, editor);
} break;
case Variant::POOL_BYTE_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_BYTE_ARRAY);
add_property_editor(p_path, editor);
} break; // 20
case Variant::POOL_INT_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_INT_ARRAY);
add_property_editor(p_path, editor);
} break;
case Variant::POOL_REAL_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_REAL_ARRAY);
add_property_editor(p_path, editor);
} break;
case Variant::POOL_STRING_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_STRING_ARRAY);
add_property_editor(p_path, editor);
} break;
case Variant::POOL_VECTOR2_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR2_ARRAY);
add_property_editor(p_path, editor);
} break;
case Variant::POOL_VECTOR3_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR3_ARRAY);
add_property_editor(p_path, editor);
} break; // 25
case Variant::POOL_COLOR_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_COLOR_ARRAY);
add_property_editor(p_path, editor);
} break;
default: {}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 734ca2de82..0afb1bf955 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -178,6 +178,7 @@ protected:
public:
void setup(const Vector<String> &p_options);
virtual void update_property();
+ void set_option_button_clip(bool p_enable);
EditorPropertyEnum();
};
@@ -277,16 +278,26 @@ public:
class EditorPropertyEasing : public EditorProperty {
GDCLASS(EditorPropertyEasing, EditorProperty)
Control *easing_draw;
- ToolButton *button_out, *button_in, *button_linear, *button_constant;
- ToolButton *button_in_out, *button_out_in;
- VBoxContainer *vb;
+ PopupMenu *preset;
+ bool full;
+
+ enum {
+ EASING_ZERO,
+ EASING_LINEAR,
+ EASING_IN,
+ EASING_OUT,
+ EASING_IN_OUT,
+ EASING_OUT_IN,
+ EASING_MAX
+
+ };
bool flip;
void _drag_easing(const Ref<InputEvent> &p_ev);
void _draw_easing();
void _notification(int p_what);
- void _set_preset(float p_val);
+ void _set_preset(int);
protected:
static void _bind_methods();
@@ -304,6 +315,7 @@ class EditorPropertyVector2 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -319,6 +331,7 @@ class EditorPropertyRect2 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -334,6 +347,7 @@ class EditorPropertyVector3 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -349,6 +363,7 @@ class EditorPropertyPlane : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -364,6 +379,7 @@ class EditorPropertyQuat : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -379,6 +395,7 @@ class EditorPropertyAABB : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -394,6 +411,7 @@ class EditorPropertyTransform2D : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -409,6 +427,7 @@ class EditorPropertyBasis : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -424,6 +443,7 @@ class EditorPropertyTransform : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -453,6 +473,7 @@ class EditorPropertyNodePath : public EditorProperty {
SceneTreeDialog *scene_tree;
NodePath base_hint;
+ Vector<StringName> valid_types;
void _node_selected(const NodePath &p_path);
void _node_assign();
void _node_clear();
@@ -463,7 +484,7 @@ protected:
public:
virtual void update_property();
- void setup(const NodePath &p_base_hint);
+ void setup(const NodePath &p_base_hint, Vector<StringName> p_valid_types);
EditorPropertyNodePath();
};
@@ -486,6 +507,7 @@ class EditorPropertyResource : public EditorProperty {
};
Button *assign;
+ TextureRect *preview;
Button *edit;
PopupMenu *menu;
EditorFileDialog *file;
@@ -530,6 +552,8 @@ public:
void collapse_all_folding();
void expand_all_folding();
+ void set_use_sub_inspector(bool p_enable);
+
EditorPropertyResource();
};
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 90f8d0e157..2bd28170e7 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -172,28 +172,9 @@ void EditorPropertyArray::update_property() {
Variant array = get_edited_object()->get(get_edited_property());
- if ((!array.is_array()) != edit->is_disabled()) {
-
- if (array.is_array()) {
- edit->set_disabled(false);
- edit->set_pressed(false);
-
- } else {
- edit->set_disabled(true);
- if (vbox) {
- memdelete(vbox);
- }
- }
- }
-
- if (!array.is_array()) {
- return;
- }
-
- String arrtype;
- switch (array.get_type()) {
+ String arrtype = "";
+ switch (array_type) {
case Variant::ARRAY: {
-
arrtype = "Array";
} break;
@@ -229,6 +210,15 @@ void EditorPropertyArray::update_property() {
default: {}
}
+ if (!array.is_array()) {
+ edit->set_text(arrtype + "[" + Variant::get_type_name(array.get_type()) + "]");
+ edit->set_pressed(false);
+ if (vbox) {
+ memdelete(vbox);
+ }
+ return;
+ }
+
edit->set_text(arrtype + "[" + itos(array.call("size")) + "]");
#ifdef TOOLS_ENABLED
@@ -419,40 +409,55 @@ void EditorPropertyArray::update_property() {
prop = memnew(EditorPropertyDictionary);
} break;
- case Variant::ARRAY: {
- prop = memnew(EditorPropertyArray);
+ // arrays
+ case Variant::ARRAY: {
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::ARRAY);
+ prop = editor;
} break;
-
- // arrays
case Variant::POOL_BYTE_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_BYTE_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_INT_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_INT_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_REAL_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_REAL_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_STRING_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_STRING_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_VECTOR2_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR2_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_VECTOR3_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_VECTOR3_ARRAY);
+ prop = editor;
} break;
case Variant::POOL_COLOR_ARRAY: {
- prop = memnew(EditorPropertyArray);
+ EditorPropertyArray *editor = memnew(EditorPropertyArray);
+ editor->setup(Variant::POOL_COLOR_ARRAY);
+ prop = editor;
} break;
default: {}
}
@@ -496,6 +501,14 @@ void EditorPropertyArray::_notification(int p_what) {
}
void EditorPropertyArray::_edit_pressed() {
+ Variant array = get_edited_object()->get(get_edited_property());
+ if (!array.is_array()) {
+ Variant::CallError ce;
+ array = Variant::construct(array_type, NULL, 0, ce);
+
+ get_edited_object()->set(get_edited_property(), array);
+ }
+
get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
update_property();
}
@@ -522,6 +535,11 @@ void EditorPropertyArray::_length_changed(double p_page) {
update_property();
}
+void EditorPropertyArray::setup(Variant::Type p_array_type) {
+
+ array_type = p_array_type;
+}
+
void EditorPropertyArray::_bind_methods() {
ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed);
ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed);
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 7f6203ee88..75c67d280d 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -62,6 +62,7 @@ class EditorPropertyArray : public EditorProperty {
EditorSpinSlider *length;
EditorSpinSlider *page;
HBoxContainer *page_hb;
+ Variant::Type array_type;
void _page_changed(double p_page);
void _length_changed(double p_page);
@@ -75,6 +76,7 @@ protected:
void _notification(int p_what);
public:
+ void setup(Variant::Type p_array_type);
virtual void update_property();
EditorPropertyArray();
};
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
new file mode 100644
index 0000000000..72050cd79b
--- /dev/null
+++ b/editor/editor_sectioned_inspector.cpp
@@ -0,0 +1,306 @@
+#include "editor_sectioned_inspector.h"
+#include "editor_scale.h"
+class SectionedInspectorFilter : public Object {
+
+ GDCLASS(SectionedInspectorFilter, Object);
+
+ Object *edited;
+ String section;
+ bool allow_sub;
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+
+ if (!edited)
+ return false;
+
+ String name = p_name;
+ if (section != "") {
+ name = section + "/" + name;
+ }
+
+ bool valid;
+ edited->set(name, p_value, &valid);
+ return valid;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+
+ if (!edited)
+ return false;
+
+ String name = p_name;
+ if (section != "") {
+ name = section + "/" + name;
+ }
+
+ bool valid = false;
+
+ r_ret = edited->get(name, &valid);
+ return valid;
+ }
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+
+ if (!edited)
+ return;
+
+ List<PropertyInfo> pinfo;
+ edited->get_property_list(&pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+
+ PropertyInfo pi = E->get();
+ int sp = pi.name.find("/");
+
+ if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/")) //skip resource stuff
+ continue;
+
+ if (sp == -1) {
+ pi.name = "global/" + pi.name;
+ }
+
+ if (pi.name.begins_with(section + "/")) {
+ pi.name = pi.name.replace_first(section + "/", "");
+ if (!allow_sub && pi.name.find("/") != -1)
+ continue;
+ p_list->push_back(pi);
+ }
+ }
+ }
+
+ bool property_can_revert(const String &p_name) {
+
+ return edited->call("property_can_revert", section + "/" + p_name);
+ }
+
+ Variant property_get_revert(const String &p_name) {
+
+ return edited->call("property_get_revert", section + "/" + p_name);
+ }
+
+protected:
+ static void _bind_methods() {
+
+ ClassDB::bind_method("property_can_revert", &SectionedInspectorFilter::property_can_revert);
+ ClassDB::bind_method("property_get_revert", &SectionedInspectorFilter::property_get_revert);
+ }
+
+public:
+ void set_section(const String &p_section, bool p_allow_sub) {
+
+ section = p_section;
+ allow_sub = p_allow_sub;
+ _change_notify();
+ }
+
+ void set_edited(Object *p_edited) {
+ edited = p_edited;
+ _change_notify();
+ }
+
+ SectionedInspectorFilter() {
+ edited = NULL;
+ }
+};
+
+void SectionedInspector::_bind_methods() {
+
+ ClassDB::bind_method("_section_selected", &SectionedInspector::_section_selected);
+ ClassDB::bind_method("_search_changed", &SectionedInspector::_search_changed);
+
+ ClassDB::bind_method("update_category_list", &SectionedInspector::update_category_list);
+}
+
+void SectionedInspector::_section_selected() {
+
+ if (!sections->get_selected())
+ return;
+
+ filter->set_section(sections->get_selected()->get_metadata(0), sections->get_selected()->get_children() == NULL);
+ inspector->set_property_prefix(String(sections->get_selected()->get_metadata(0)) + "/");
+}
+
+void SectionedInspector::set_current_section(const String &p_section) {
+
+ if (section_map.has(p_section)) {
+ section_map[p_section]->select(0);
+ }
+}
+
+String SectionedInspector::get_current_section() const {
+
+ if (sections->get_selected())
+ return sections->get_selected()->get_metadata(0);
+ else
+ return "";
+}
+
+String SectionedInspector::get_full_item_path(const String &p_item) {
+
+ String base = get_current_section();
+
+ if (base != "")
+ return base + "/" + p_item;
+ else
+ return p_item;
+}
+
+void SectionedInspector::edit(Object *p_object) {
+
+ if (!p_object) {
+ obj = -1;
+ sections->clear();
+
+ filter->set_edited(NULL);
+ inspector->edit(NULL);
+
+ return;
+ }
+
+ ObjectID id = p_object->get_instance_id();
+
+ inspector->set_object_class(p_object->get_class());
+
+ if (obj != id) {
+
+ obj = id;
+ update_category_list();
+
+ filter->set_edited(p_object);
+ inspector->edit(filter);
+
+ if (sections->get_root()->get_children()) {
+ sections->get_root()->get_children()->select(0);
+ }
+ } else {
+
+ update_category_list();
+ }
+}
+
+void SectionedInspector::update_category_list() {
+
+ String selected_category = get_current_section();
+ sections->clear();
+
+ Object *o = ObjectDB::get_instance(obj);
+
+ if (!o)
+ return;
+
+ List<PropertyInfo> pinfo;
+ o->get_property_list(&pinfo);
+
+ section_map.clear();
+
+ TreeItem *root = sections->create_item();
+ section_map[""] = root;
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+
+ PropertyInfo pi = E->get();
+
+ if (pi.usage & PROPERTY_USAGE_CATEGORY)
+ continue;
+ else if (!(pi.usage & PROPERTY_USAGE_EDITOR))
+ continue;
+
+ if (pi.name.find(":") != -1 || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene")
+ continue;
+
+ if (search_box && search_box->get_text() != String() && pi.name.findn(search_box->get_text()) == -1)
+ continue;
+
+ int sp = pi.name.find("/");
+ if (sp == -1)
+ pi.name = "Global/" + pi.name;
+
+ Vector<String> sectionarr = pi.name.split("/");
+ String metasection;
+
+ int sc = MIN(2, sectionarr.size() - 1);
+
+ for (int i = 0; i < sc; i++) {
+
+ TreeItem *parent = section_map[metasection];
+ parent->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
+
+ if (i > 0) {
+ metasection += "/" + sectionarr[i];
+ } else {
+ metasection = sectionarr[i];
+ }
+
+ if (!section_map.has(metasection)) {
+ TreeItem *ms = sections->create_item(parent);
+ section_map[metasection] = ms;
+ ms->set_text(0, sectionarr[i].capitalize());
+ ms->set_metadata(0, metasection);
+ ms->set_selectable(0, false);
+ }
+
+ if (i == sc - 1) {
+ //if it has children, make selectable
+ section_map[metasection]->set_selectable(0, true);
+ }
+ }
+ }
+
+ if (section_map.has(selected_category)) {
+ section_map[selected_category]->select(0);
+ }
+
+ inspector->update_tree();
+}
+
+void SectionedInspector::register_search_box(LineEdit *p_box) {
+
+ search_box = p_box;
+ inspector->register_text_enter(p_box);
+ search_box->connect("text_changed", this, "_search_changed");
+}
+
+void SectionedInspector::_search_changed(const String &p_what) {
+
+ update_category_list();
+}
+
+EditorInspector *SectionedInspector::get_inspector() {
+
+ return inspector;
+}
+
+SectionedInspector::SectionedInspector() {
+
+ obj = -1;
+
+ search_box = NULL;
+
+ add_constant_override("autohide", 1); // Fixes the dragger always showing up
+
+ VBoxContainer *left_vb = memnew(VBoxContainer);
+ left_vb->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
+ add_child(left_vb);
+
+ sections = memnew(Tree);
+ sections->set_v_size_flags(SIZE_EXPAND_FILL);
+ sections->set_hide_root(true);
+
+ left_vb->add_child(sections, true);
+
+ VBoxContainer *right_vb = memnew(VBoxContainer);
+ right_vb->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
+ right_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(right_vb);
+
+ filter = memnew(SectionedInspectorFilter);
+ inspector = memnew(EditorInspector);
+ inspector->set_v_size_flags(SIZE_EXPAND_FILL);
+ right_vb->add_child(inspector, true);
+ inspector->set_use_doc_hints(true);
+
+ sections->connect("cell_selected", this, "_section_selected");
+}
+
+SectionedInspector::~SectionedInspector() {
+
+ memdelete(filter);
+}
diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h
new file mode 100644
index 0000000000..75b51a1581
--- /dev/null
+++ b/editor/editor_sectioned_inspector.h
@@ -0,0 +1,42 @@
+#ifndef EDITOR_SECTIONED_INSPECTOR_H
+#define EDITOR_SECTIONED_INSPECTOR_H
+
+#include "editor/editor_inspector.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tree.h"
+
+class SectionedInspectorFilter;
+
+class SectionedInspector : public HSplitContainer {
+
+ GDCLASS(SectionedInspector, HSplitContainer);
+
+ ObjectID obj;
+
+ Tree *sections;
+ SectionedInspectorFilter *filter;
+
+ Map<String, TreeItem *> section_map;
+ EditorInspector *inspector;
+ LineEdit *search_box;
+
+ static void _bind_methods();
+ void _section_selected();
+
+ void _search_changed(const String &p_what);
+
+public:
+ void register_search_box(LineEdit *p_box);
+ EditorInspector *get_inspector();
+ void edit(Object *p_object);
+ String get_full_item_path(const String &p_item);
+
+ void set_current_section(const String &p_section);
+ String get_current_section() const;
+
+ void update_category_list();
+
+ SectionedInspector();
+ ~SectionedInspector();
+};
+#endif // EDITOR_SECTIONED_INSPECTOR_H
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index ff644ad126..466b12157d 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -165,6 +165,7 @@ struct _EVCSort {
Variant::Type type;
int order;
bool save;
+ bool restart_if_changed;
bool operator<(const _EVCSort &p_vcs) const { return order < p_vcs.order; }
};
@@ -188,6 +189,7 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const {
vc.order = v->order;
vc.type = v->variant.get_type();
vc.save = v->save;
+ vc.restart_if_changed = v->restart_if_changed;
vclist.insert(vc);
}
@@ -210,6 +212,10 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const {
if (hints.has(E->get().name))
pi = hints[E->get().name];
+ if (E->get().restart_if_changed) {
+ pi.usage |= PROPERTY_USAGE_RESTART_IF_CHANGED;
+ }
+
p_list->push_back(pi);
}
@@ -280,6 +286,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
}
_initial_set("interface/editor/editor_language", best);
+ set_restart_if_changed("interface/editor/editor_language", true);
hints["interface/editor/editor_language"] = PropertyInfo(Variant::STRING, "interface/editor/editor_language", PROPERTY_HINT_ENUM, lang_hint, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
}
@@ -313,8 +320,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
_initial_set("interface/editor/quit_confirmation", true);
- _initial_set("interface/theme/preset", 0);
- hints["interface/theme/preset"] = PropertyInfo(Variant::INT, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Custom,Grey,Godot 2,Arc,Light,Alien,Solarized (Dark),Solarized (Light)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ _initial_set("interface/theme/preset", "Default");
+ hints["interface/theme/preset"] = PropertyInfo(Variant::STRING, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Alien,Arc,Godot 2,Grey,Light,Solarized (Dark),Solarized (Light),Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/theme/icon_and_font_color", 0);
hints["interface/theme/icon_and_font_color"] = PropertyInfo(Variant::INT, "interface/theme/icon_and_font_color", PROPERTY_HINT_ENUM, "Auto,Dark,Light", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/theme/base_color", Color::html("#323b4f"));
@@ -357,6 +364,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/highlighting/highlight_all_occurrences", true);
_initial_set("text_editor/highlighting/highlight_current_line", true);
+ _initial_set("text_editor/highlighting/highlight_type_safe_lines", true);
_initial_set("text_editor/cursor/scroll_past_end_of_file", false);
_initial_set("text_editor/indent/type", 0);
@@ -397,6 +405,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/completion/callhint_tooltip_offset", Vector2());
_initial_set("text_editor/files/restore_scripts_on_load", true);
_initial_set("text_editor/completion/complete_file_paths", true);
+ _initial_set("text_editor/completion/add_type_hints", false);
_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
_initial_set("docks/scene_tree/draw_relationship_lines", false);
@@ -585,6 +594,7 @@ void EditorSettings::_load_default_text_editor_theme() {
_initial_set("text_editor/highlighting/completion_font_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/text_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/line_number_color", Color::html("66aaaaaa"));
+ _initial_set("text_editor/highlighting/safe_line_number_color", Color::html("99aac8aa"));
_initial_set("text_editor/highlighting/caret_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/caret_background_color", Color::html("000000"));
_initial_set("text_editor/highlighting/text_selected_color", Color::html("000000"));
@@ -1017,6 +1027,14 @@ void EditorSettings::raise_order(const String &p_setting) {
props[p_setting].order = ++last_order;
}
+void EditorSettings::set_restart_if_changed(const StringName &p_setting, bool p_restart) {
+ _THREAD_SAFE_METHOD_
+
+ if (!props.has(p_setting))
+ return;
+ props[p_setting].restart_if_changed = p_restart;
+}
+
void EditorSettings::set_initial_value(const StringName &p_setting, const Variant &p_value, bool p_update_current) {
_THREAD_SAFE_METHOD_
@@ -1030,16 +1048,19 @@ void EditorSettings::set_initial_value(const StringName &p_setting, const Varian
}
}
-Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default) {
+Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_restart_if_changed) {
Variant ret = p_default;
- if (EditorSettings::get_singleton()->has_setting(p_setting))
+ if (EditorSettings::get_singleton()->has_setting(p_setting)) {
ret = EditorSettings::get_singleton()->get(p_setting);
- else
+ } else {
EditorSettings::get_singleton()->set_manually(p_setting, p_default);
+ EditorSettings::get_singleton()->set_restart_if_changed(p_setting, p_restart_if_changed);
+ }
- if (!EditorSettings::get_singleton()->has_default_value(p_setting))
+ if (!EditorSettings::get_singleton()->has_default_value(p_setting)) {
EditorSettings::get_singleton()->set_initial_value(p_setting, p_default);
+ }
return ret;
}
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 420e067cad..e5b61abf54 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -70,6 +70,7 @@ private:
bool has_default_value;
bool hide_from_editor;
bool save;
+ bool restart_if_changed;
VariantContainer() {
variant = Variant();
initial = Variant();
@@ -77,6 +78,7 @@ private:
hide_from_editor = false;
has_default_value = false;
save = false;
+ restart_if_changed = false;
}
VariantContainer(const Variant &p_variant, int p_order) {
variant = p_variant;
@@ -85,6 +87,7 @@ private:
hide_from_editor = false;
has_default_value = false;
save = false;
+ restart_if_changed = false;
}
};
@@ -145,6 +148,7 @@ public:
void erase(const String &p_setting);
void raise_order(const String &p_setting);
void set_initial_value(const StringName &p_setting, const Variant &p_value, bool p_update_current = false);
+ void set_restart_if_changed(const StringName &p_setting, bool p_restart);
void set_manually(const StringName &p_setting, const Variant &p_value, bool p_emit_signal = false) {
if (p_emit_signal)
_set(p_setting, p_value);
@@ -200,7 +204,8 @@ public:
//not a macro any longer
#define EDITOR_DEF(m_var, m_val) _EDITOR_DEF(m_var, Variant(m_val))
-Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default);
+#define EDITOR_DEF_RST(m_var, m_val) _EDITOR_DEF(m_var, Variant(m_val), true)
+Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_restart_if_changed = false);
#define EDITOR_GET(m_var) _EDITOR_GET(m_var)
Variant _EDITOR_GET(const String &p_setting);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index 0852a42794..0e6d81d13b 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -56,6 +56,7 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
} else {
grabbing_spinner_attempt = true;
+ grabbing_spinner_dist_cache = 0;
grabbing_spinner = false;
grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position();
}
@@ -69,13 +70,7 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
Input::get_singleton()->warp_mouse_position(grabbing_spinner_mouse_pos);
update();
} else {
- Rect2 gr = get_global_rect();
- value_input->set_text(get_text_value());
- value_input->set_position(gr.position);
- value_input->set_size(gr.size);
- value_input->call_deferred("show_modal");
- value_input->call_deferred("grab_focus");
- value_input->call_deferred("select_all");
+ _focus_entered();
}
grabbing_spinner = false;
@@ -89,21 +84,29 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
if (grabbing_spinner_attempt) {
- if (!grabbing_spinner) {
+ double diff_x = mm->get_relative().x;
+ if (mm->get_shift() && grabbing_spinner) {
+ diff_x *= 0.1;
+ }
+ grabbing_spinner_dist_cache += diff_x;
+
+ if (!grabbing_spinner && ABS(grabbing_spinner_dist_cache) > 4 * EDSCALE) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
grabbing_spinner = true;
}
- double v = get_value();
-
- double diff_x = mm->get_relative().x;
- diff_x = Math::pow(ABS(diff_x), 1.8) * SGN(diff_x);
- diff_x *= 0.1;
-
- v += diff_x * get_step();
-
- set_value(v);
-
+ if (grabbing_spinner) {
+ if (mm->get_control() || updown_offset != -1) {
+ set_value(Math::round(get_value()));
+ if (ABS(grabbing_spinner_dist_cache) > 6) {
+ set_value(get_value() + SGN(grabbing_spinner_dist_cache));
+ grabbing_spinner_dist_cache = 0;
+ }
+ } else {
+ set_value(get_value() + get_step() * grabbing_spinner_dist_cache * 10);
+ grabbing_spinner_dist_cache = 0;
+ }
+ }
} else if (updown_offset != -1) {
bool new_hover = (mm->get_position().x > updown_offset);
if (new_hover != hover_updown) {
@@ -115,25 +118,10 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept")) {
- Rect2 gr = get_global_rect();
- value_input->set_text(get_text_value());
- value_input->set_position(gr.position);
- value_input->set_size(gr.size);
- value_input->call_deferred("show_modal");
- value_input->call_deferred("grab_focus");
- value_input->call_deferred("select_all");
+ _focus_entered();
}
}
-void EditorSpinSlider::_value_input_closed() {
- set_value(value_input->get_text().to_double());
-}
-
-void EditorSpinSlider::_value_input_entered(const String &p_text) {
- set_value(p_text.to_double());
- value_input->hide();
-}
-
void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
@@ -173,20 +161,20 @@ void EditorSpinSlider::_notification(int p_what) {
updown_offset = -1;
Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
- draw_style_box(sb, Rect2(Vector2(), get_size()));
+ if (!flat) {
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+ }
Ref<Font> font = get_font("font", "LineEdit");
+ int sep_base = 4 * EDSCALE;
+ int sep = sep_base + sb->get_offset().x; //make it have the same margin on both sides, looks better
+
+ int string_width = font->get_string_size(label).width;
+ int number_width = get_size().width - sb->get_minimum_size().width - string_width - sep;
- int avail_width = get_size().width - sb->get_minimum_size().width - sb->get_minimum_size().width;
- avail_width -= font->get_string_size(label).width;
Ref<Texture> updown = get_icon("updown", "SpinBox");
if (get_step() == 1) {
- avail_width -= updown->get_width();
- }
-
- if (has_focus()) {
- Ref<StyleBox> focus = get_stylebox("focus", "LineEdit");
- draw_style_box(focus, Rect2(Vector2(), get_size()));
+ number_width -= updown->get_width();
}
String numstr = get_text_value();
@@ -194,10 +182,26 @@ void EditorSpinSlider::_notification(int p_what) {
int vofs = (get_size().height - font->get_height()) / 2 + font->get_ascent();
Color fc = get_color("font_color", "LineEdit");
+ Color lc;
+ if (use_custom_label_color) {
+ lc = custom_label_color;
+ } else {
+ lc = fc;
+ }
- int label_ofs = sb->get_offset().x + avail_width;
- draw_string(font, Vector2(label_ofs, vofs), label, fc * Color(1, 1, 1, 0.5));
- draw_string(font, Vector2(sb->get_offset().x, vofs), numstr, fc, avail_width);
+ if (flat && label != String()) {
+ Color label_bg_color = get_color("dark_color_3", "Editor");
+ draw_rect(Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + string_width, get_size().height)), label_bg_color);
+ }
+
+ if (has_focus()) {
+ Ref<StyleBox> focus = get_stylebox("focus", "LineEdit");
+ draw_style_box(focus, Rect2(Vector2(), get_size()));
+ }
+
+ draw_string(font, Vector2(sb->get_offset().x, vofs), label, lc * Color(1, 1, 1, 0.5));
+
+ draw_string(font, Vector2(sb->get_offset().x + string_width + sep, vofs), numstr, fc, number_width);
if (get_step() == 1) {
Ref<Texture> updown = get_icon("updown", "SpinBox");
@@ -225,7 +229,7 @@ void EditorSpinSlider::_notification(int p_what) {
Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE);
draw_rect(grabber_rect, c);
- bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner;
+ bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !value_input->is_visible();
if (grabber->is_visible() != display_grabber) {
if (display_grabber) {
grabber->show();
@@ -263,6 +267,15 @@ void EditorSpinSlider::_notification(int p_what) {
mouse_over_spin = false;
update();
}
+ if (p_what == NOTIFICATION_FOCUS_ENTER) {
+ /* Sorry, I dont like this, it makes navigating the different fields with arrows more difficult.
+ * Just press enter to edit.
+ * if (!Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) {
+ _focus_entered();
+ }*/
+
+ value_input_just_closed = false;
+ }
}
Size2 EditorSpinSlider::get_minimum_size() const {
@@ -294,6 +307,34 @@ String EditorSpinSlider::get_label() const {
return label;
}
+//text_entered signal
+void EditorSpinSlider::_value_input_entered(const String &p_text) {
+ value_input_just_closed = true;
+ value_input->hide();
+}
+
+//modal_closed signal
+void EditorSpinSlider::_value_input_closed() {
+ set_value(value_input->get_text().to_double());
+ value_input_just_closed = true;
+}
+
+//focus_exited signal
+void EditorSpinSlider::_value_focus_exited() {
+ set_value(value_input->get_text().to_double());
+ // focus is not on the same element after the vlalue_input was exited
+ // -> focus is on next element
+ // -> TAB was pressed
+ // -> modal_close was not called
+ // -> need to close/hide manually
+ if (!value_input_just_closed) { //value_input_just_closed should do the same
+ value_input->hide();
+ //tab was pressed
+ } else {
+ //enter, click, esc
+ }
+}
+
void EditorSpinSlider::_grabber_mouse_entered() {
mouse_over_grabber = true;
update();
@@ -314,6 +355,33 @@ bool EditorSpinSlider::is_read_only() const {
return read_only;
}
+void EditorSpinSlider::set_flat(bool p_enable) {
+
+ flat = p_enable;
+ update();
+}
+
+bool EditorSpinSlider::is_flat() const {
+ return flat;
+}
+
+void EditorSpinSlider::set_custom_label_color(bool p_use_custom_label_color, Color p_custom_label_color) {
+ use_custom_label_color = p_use_custom_label_color;
+ custom_label_color = p_custom_label_color;
+}
+
+void EditorSpinSlider::_focus_entered() {
+ Rect2 gr = get_global_rect();
+ value_input->set_text(get_text_value());
+ value_input->set_position(gr.position);
+ value_input->set_size(gr.size);
+ value_input->call_deferred("show_modal");
+ value_input->call_deferred("grab_focus");
+ value_input->call_deferred("select_all");
+ value_input->set_focus_next(find_next_valid_focus()->get_path());
+ value_input->set_focus_previous(find_prev_valid_focus()->get_path());
+}
+
void EditorSpinSlider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_label", "label"), &EditorSpinSlider::set_label);
ClassDB::bind_method(D_METHOD("get_label"), &EditorSpinSlider::get_label);
@@ -321,22 +389,28 @@ void EditorSpinSlider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_read_only", "read_only"), &EditorSpinSlider::set_read_only);
ClassDB::bind_method(D_METHOD("is_read_only"), &EditorSpinSlider::is_read_only);
+ ClassDB::bind_method(D_METHOD("set_flat", "flat"), &EditorSpinSlider::set_flat);
+ ClassDB::bind_method(D_METHOD("is_flat"), &EditorSpinSlider::is_flat);
+
ClassDB::bind_method(D_METHOD("_gui_input"), &EditorSpinSlider::_gui_input);
ClassDB::bind_method(D_METHOD("_grabber_mouse_entered"), &EditorSpinSlider::_grabber_mouse_entered);
ClassDB::bind_method(D_METHOD("_grabber_mouse_exited"), &EditorSpinSlider::_grabber_mouse_exited);
ClassDB::bind_method(D_METHOD("_grabber_gui_input"), &EditorSpinSlider::_grabber_gui_input);
ClassDB::bind_method(D_METHOD("_value_input_closed"), &EditorSpinSlider::_value_input_closed);
ClassDB::bind_method(D_METHOD("_value_input_entered"), &EditorSpinSlider::_value_input_entered);
+ ClassDB::bind_method(D_METHOD("_value_focus_exited"), &EditorSpinSlider::_value_focus_exited);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
}
EditorSpinSlider::EditorSpinSlider() {
+ flat = false;
grabbing_spinner_attempt = false;
grabbing_spinner = false;
-
+ grabbing_spinner_dist_cache = 0;
set_focus_mode(FOCUS_ALL);
updown_offset = -1;
hover_updown = false;
@@ -358,6 +432,9 @@ EditorSpinSlider::EditorSpinSlider() {
value_input->hide();
value_input->connect("modal_closed", this, "_value_input_closed");
value_input->connect("text_entered", this, "_value_input_entered");
+ value_input->connect("focus_exited", this, "_value_focus_exited");
+ value_input_just_closed = false;
hide_slider = false;
read_only = false;
+ use_custom_label_color = false;
}
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
index 37d8a5f128..fb32534ef4 100644
--- a/editor/editor_spin_slider.h
+++ b/editor/editor_spin_slider.h
@@ -57,15 +57,21 @@ class EditorSpinSlider : public Range {
bool grabbing_spinner;
bool read_only;
+ float grabbing_spinner_dist_cache;
Vector2 grabbing_spinner_mouse_pos;
LineEdit *value_input;
+ bool value_input_just_closed;
void _grabber_gui_input(const Ref<InputEvent> &p_event);
void _value_input_closed();
void _value_input_entered(const String &);
-
+ void _value_focus_exited();
bool hide_slider;
+ bool flat;
+
+ bool use_custom_label_color;
+ Color custom_label_color;
protected:
void _notification(int p_what);
@@ -73,6 +79,7 @@ protected:
static void _bind_methods();
void _grabber_mouse_entered();
void _grabber_mouse_exited();
+ void _focus_entered();
public:
String get_text_value() const;
@@ -85,6 +92,11 @@ public:
void set_read_only(bool p_enable);
bool is_read_only() const;
+ void set_flat(bool p_enable);
+ bool is_flat() const;
+
+ void set_custom_label_color(bool p_use_custom_label_color, Color p_custom_label_color);
+
virtual Size2 get_minimum_size() const;
EditorSpinSlider();
};
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 78aa3da546..ea9f6db61a 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -72,10 +72,11 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left =
return style;
}
-static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow = 1, bool p_vertical = false) {
+static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow_begin = 1, float p_grow_end = 1, bool p_vertical = false) {
Ref<StyleBoxLine> style(memnew(StyleBoxLine));
style->set_color(p_color);
- style->set_grow(p_grow);
+ style->set_grow_begin(p_grow_begin);
+ style->set_grow_end(p_grow_end);
style->set_thickness(p_thickness);
style->set_vertical(p_vertical);
return style;
@@ -254,7 +255,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color base_color = EDITOR_DEF("interface/theme/base_color", Color::html("#323b4f"));
float contrast = EDITOR_DEF("interface/theme/contrast", default_contrast);
- int preset = EDITOR_DEF("interface/theme/preset", 0);
+ String preset = EDITOR_DEF("interface/theme/preset", "Default");
+
int icon_font_color_setting = EDITOR_DEF("interface/theme/icon_and_font_color", 0);
bool highlight_tabs = EDITOR_DEF("interface/theme/highlight_tabs", false);
int border_size = EDITOR_DEF("interface/theme/border_size", 1);
@@ -266,55 +268,52 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color preset_accent_color;
Color preset_base_color;
float preset_contrast;
- switch (preset) {
- case 0: { // Default
- preset_accent_color = Color::html("#699ce8");
- preset_base_color = Color::html("#323b4f");
- preset_contrast = default_contrast;
- } break;
- case 1: { // Custom
- accent_color = EDITOR_DEF("interface/theme/accent_color", Color::html("#699ce8"));
- base_color = EDITOR_DEF("interface/theme/base_color", Color::html("#323b4f"));
- contrast = EDITOR_DEF("interface/theme/contrast", default_contrast);
- } break;
- case 2: { // Grey
- preset_accent_color = Color::html("#b8e4ff");
- preset_base_color = Color::html("#3d3d3d");
- preset_contrast = 0.2;
- } break;
- case 3: { // Godot 2
- preset_accent_color = Color::html("#86ace2");
- preset_base_color = Color::html("#3C3A44");
- preset_contrast = 0.25;
- } break;
- case 4: { // Arc
- preset_accent_color = Color::html("#5294e2");
- preset_base_color = Color::html("#383c4a");
- preset_contrast = 0.25;
- } break;
- case 5: { // Light
- preset_accent_color = Color::html("#2070ff");
- preset_base_color = Color::html("#ffffff");
- preset_contrast = 0.08;
- } break;
- case 6: { // Alien
- preset_accent_color = Color::html("#1bfe99");
- preset_base_color = Color::html("#2f373f");
- preset_contrast = 0.25;
- } break;
- case 7: { // Solarized (Dark)
- preset_accent_color = Color::html("#268bd2");
- preset_base_color = Color::html("#073642");
- preset_contrast = 0.15;
- } break;
- case 8: { // Solarized (Light)
- preset_accent_color = Color::html("#268bd2");
- preset_base_color = Color::html("#fdf6e3");
- preset_contrast = 0.06;
- } break;
+
+ // Please, use alphabet order if you've added new theme here(After "Default" and "Custom")
+
+ if (preset == "Default") {
+ preset_accent_color = Color::html("#699ce8");
+ preset_base_color = Color::html("#323b4f");
+ preset_contrast = default_contrast;
+ } else if (preset == "Custom") {
+ accent_color = EDITOR_DEF("interface/theme/accent_color", Color::html("#699ce8"));
+ base_color = EDITOR_DEF("interface/theme/base_color", Color::html("#323b4f"));
+ contrast = EDITOR_DEF("interface/theme/contrast", default_contrast);
+ } else if (preset == "Alien") {
+ preset_accent_color = Color::html("#1bfe99");
+ preset_base_color = Color::html("#2f373f");
+ preset_contrast = 0.25;
+ } else if (preset == "Arc") {
+ preset_accent_color = Color::html("#5294e2");
+ preset_base_color = Color::html("#383c4a");
+ preset_contrast = 0.25;
+ } else if (preset == "Godot 2") {
+ preset_accent_color = Color::html("#86ace2");
+ preset_base_color = Color::html("#3C3A44");
+ preset_contrast = 0.25;
+ } else if (preset == "Grey") {
+ preset_accent_color = Color::html("#b8e4ff");
+ preset_base_color = Color::html("#3d3d3d");
+ preset_contrast = 0.2;
+ } else if (preset == "Light") {
+ preset_accent_color = Color::html("#2070ff");
+ preset_base_color = Color::html("#ffffff");
+ preset_contrast = 0.08;
+ } else if (preset == "Solarized (Dark)") {
+ preset_accent_color = Color::html("#268bd2");
+ preset_base_color = Color::html("#073642");
+ preset_contrast = 0.15;
+ } else if (preset == "Solarized (Light)") {
+ preset_accent_color = Color::html("#268bd2");
+ preset_base_color = Color::html("#fdf6e3");
+ preset_contrast = 0.06;
+ } else { // Default
+ preset_accent_color = Color::html("#699ce8");
+ preset_base_color = Color::html("#323b4f");
+ preset_contrast = default_contrast;
}
- if (preset != 1) {
+ if (preset != "Custom") {
accent_color = preset_accent_color;
base_color = preset_base_color;
contrast = preset_contrast;
@@ -464,9 +463,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine));
style_popup_separator->set_color(separator_color);
- style_popup_separator->set_grow(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_separator->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_separator->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
style_popup_separator->set_thickness(MAX(EDSCALE, border_width));
+ Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine));
+ style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_left->set_color(separator_color);
+ style_popup_labeled_separator_left->set_thickness(MAX(EDSCALE, border_width));
+
+ Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine));
+ style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_right->set_color(separator_color);
+ style_popup_labeled_separator_right->set_thickness(MAX(EDSCALE, border_width));
+
Ref<StyleBoxEmpty> style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size);
// Tabs
@@ -580,6 +590,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons"));
theme->set_constant("arrow_margin", "OptionButton", default_margin_size * EDSCALE);
theme->set_constant("modulate_arrow", "OptionButton", true);
+ theme->set_constant("hseparation", "OptionButton", 4 * EDSCALE);
// CheckButton
theme->set_stylebox("normal", "CheckButton", style_menu);
@@ -628,6 +639,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<StyleBoxFlat> style_popup_menu = style_popup;
theme->set_stylebox("panel", "PopupMenu", style_popup_menu);
theme->set_stylebox("separator", "PopupMenu", style_popup_separator);
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", style_popup_labeled_separator_left);
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", style_popup_labeled_separator_right);
+
theme->set_color("font_color", "PopupMenu", font_color);
theme->set_color("font_color_hover", "PopupMenu", font_color_hl);
theme->set_color("font_color_accel", "PopupMenu", font_color_disabled);
@@ -642,6 +656,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons"));
theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size) * EDSCALE);
+ Ref<StyleBoxFlat> sub_inspector_bg = make_flat_stylebox(dark_color_1, 2, 0, 0, 0);
+ sub_inspector_bg->set_border_width(MARGIN_LEFT, 2);
+ sub_inspector_bg->set_border_color(MARGIN_LEFT, accent_color * Color(1, 1, 1, 0.3));
+ sub_inspector_bg->set_draw_center(true);
+
+ theme->set_stylebox("sub_inspector_bg", "Editor", sub_inspector_bg);
+ theme->set_constant("inspector_margin", "Editor", 8 * EDSCALE);
+
// Tree & ItemList background
Ref<StyleBoxFlat> style_tree_bg = style_default->duplicate();
style_tree_bg->set_bg_color(dark_color_1);
@@ -652,6 +674,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Tree
theme->set_icon("checked", "Tree", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "Tree", theme->get_icon("GuiUnchecked", "EditorIcons"));
+ theme->set_icon("arrow_up", "Tree", theme->get_icon("GuiTreeArrowUp", "EditorIcons"));
theme->set_icon("arrow", "Tree", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
theme->set_icon("arrow_collapsed", "Tree", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
theme->set_icon("updown", "Tree", theme->get_icon("GuiTreeUpdown", "EditorIcons"));
@@ -782,7 +805,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Separators
theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, border_width));
- theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, true));
+ theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, 0, true));
// Debugger
@@ -1007,6 +1030,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("title_h_offset", "GraphNode", -16 * EDSCALE);
theme->set_constant("close_h_offset", "GraphNode", 20 * EDSCALE);
theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE);
+ theme->set_constant("separation", "GraphNode", 1 * EDSCALE);
+
theme->set_icon("close", "GraphNode", theme->get_icon("GuiCloseCustomizable", "EditorIcons"));
theme->set_icon("resizer", "GraphNode", theme->get_icon("GuiResizer", "EditorIcons"));
theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons"));
@@ -1064,6 +1089,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color completion_font_color = font_color;
const Color text_color = font_color;
const Color line_number_color = dim_color;
+ const Color safe_line_number_color = dim_color * Color(1, 1.2, 1, 1.5);
const Color caret_color = mono_color;
const Color caret_background_color = mono_color.inverted();
const Color text_selected_color = dark_color_3;
@@ -1098,6 +1124,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
setting->set_initial_value("text_editor/highlighting/completion_font_color", completion_font_color, true);
setting->set_initial_value("text_editor/highlighting/text_color", text_color, true);
setting->set_initial_value("text_editor/highlighting/line_number_color", line_number_color, true);
+ setting->set_initial_value("text_editor/highlighting/safe_line_number_color", safe_line_number_color, true);
setting->set_initial_value("text_editor/highlighting/caret_color", caret_color, true);
setting->set_initial_value("text_editor/highlighting/caret_background_color", caret_background_color, true);
setting->set_initial_value("text_editor/highlighting/text_selected_color", text_selected_color, true);
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index a39c8b2209..931785333f 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -123,7 +123,6 @@ void ExportTemplateManager::_update_template_list() {
void ExportTemplateManager::_download_template(const String &p_version) {
- print_line("download " + p_version);
while (template_list->get_child_count()) {
memdelete(template_list->get_child(0));
}
@@ -228,7 +227,10 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_
version = data_str;
}
- fc++;
+ if (file.get_file().size() != 0) {
+ fc++;
+ }
+
ret = unzGoToNextFile(pkg);
}
@@ -268,6 +270,11 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_
String file = String(fname).get_file();
+ if (file.size() == 0) {
+ ret = unzGoToNextFile(pkg);
+ continue;
+ }
+
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
@@ -344,7 +351,6 @@ void ExportTemplateManager::_http_download_mirror_completed(int p_status, int p_
bool mirrors_found = false;
Dictionary d = r;
- print_line(r);
if (d.has("mirrors")) {
Array mirrors = d["mirrors"];
for (int i = 0; i < mirrors.size(); i++) {
@@ -436,6 +442,10 @@ void ExportTemplateManager::_begin_template_download(const String &p_url) {
template_list_state->set_text(TTR("Connecting to Mirror..."));
}
+void ExportTemplateManager::_window_template_downloader_closed() {
+ download_templates->cancel_request();
+}
+
void ExportTemplateManager::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
@@ -495,8 +505,6 @@ void ExportTemplateManager::_notification(int p_what) {
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (!is_visible_in_tree()) {
- print_line("closed");
- download_templates->cancel_request();
set_process(false);
}
}
@@ -511,6 +519,7 @@ void ExportTemplateManager::_bind_methods() {
ClassDB::bind_method("_http_download_mirror_completed", &ExportTemplateManager::_http_download_mirror_completed);
ClassDB::bind_method("_http_download_templates_completed", &ExportTemplateManager::_http_download_templates_completed);
ClassDB::bind_method("_begin_template_download", &ExportTemplateManager::_begin_template_download);
+ ClassDB::bind_method("_window_template_downloader_closed", &ExportTemplateManager::_window_template_downloader_closed);
}
ExportTemplateManager::ExportTemplateManager() {
@@ -560,7 +569,9 @@ ExportTemplateManager::ExportTemplateManager() {
template_downloader = memnew(AcceptDialog);
template_downloader->set_title(TTR("Download Templates"));
template_downloader->get_ok()->set_text(TTR("Close"));
+ template_downloader->set_exclusive(true);
add_child(template_downloader);
+ template_downloader->connect("popup_hide", this, "_window_template_downloader_closed");
VBoxContainer *vbc = memnew(VBoxContainer);
template_downloader->add_child(vbc);
diff --git a/editor/export_template_manager.h b/editor/export_template_manager.h
index 62336162fd..54a645c69f 100644
--- a/editor/export_template_manager.h
+++ b/editor/export_template_manager.h
@@ -77,6 +77,8 @@ class ExportTemplateManager : public ConfirmationDialog {
void _begin_template_download(const String &p_url);
+ void _window_template_downloader_closed();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 297373d299..f65fb5365b 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -200,6 +200,7 @@ void FileSystemDock::_notification(int p_what) {
button_hist_next->set_icon(get_icon("Forward", ei));
button_hist_prev->set_icon(get_icon("Back", ei));
+ button_show->set_icon(get_icon("GuiVisibilityVisible", "EditorIcons"));
file_options->connect("id_pressed", this, "_file_option");
folder_options->connect("id_pressed", this, "_folder_option");
@@ -317,6 +318,15 @@ void FileSystemDock::_favorites_pressed() {
_update_tree(true);
}
+void FileSystemDock::_show_current_scene_file() {
+
+ int index = EditorNode::get_editor_data().get_edited_scene();
+ String path = EditorNode::get_editor_data().get_scene_path(index);
+ if (path != String()) {
+ navigate_to_path(path);
+ }
+}
+
String FileSystemDock::get_selected_path() const {
TreeItem *sel = tree->get_selected();
@@ -920,6 +930,21 @@ void FileSystemDock::_update_dependencies_after_move(const Map<String, String> &
}
}
+void FileSystemDock::_update_project_settings_after_move(const Map<String, String> &p_renames) const {
+
+ // Find all project settings of type FILE and replace them if needed
+ const Map<StringName, PropertyInfo> prop_info = ProjectSettings::get_singleton()->get_custom_property_info();
+ for (const Map<StringName, PropertyInfo>::Element *E = prop_info.front(); E; E = E->next()) {
+ if (E->get().hint == PROPERTY_HINT_FILE) {
+ String old_path = GLOBAL_GET(E->key());
+ if (p_renames.has(old_path)) {
+ ProjectSettings::get_singleton()->set_setting(E->key(), p_renames[old_path]);
+ }
+ };
+ }
+ ProjectSettings::get_singleton()->save();
+}
+
void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const {
Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs();
@@ -1002,6 +1027,7 @@ void FileSystemDock::_rename_operation_confirm() {
_try_move_item(to_rename, new_path, file_renames, folder_renames);
_update_dependencies_after_move(file_renames);
_update_resource_paths_after_move(file_renames);
+ _update_project_settings_after_move(file_renames);
_update_favorite_dirs_list_after_move(folder_renames);
//Rescan everything
@@ -1044,22 +1070,62 @@ void FileSystemDock::_duplicate_operation_confirm() {
_rescan();
}
-void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
+void FileSystemDock::_move_with_overwrite() {
+ _move_operation_confirm(to_move_path, true);
+}
+
+bool FileSystemDock::_check_existing() {
+ String &p_to_path = to_move_path;
+ for (int i = 0; i < to_move.size(); i++) {
+ String ol_pth = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
+ String p_new_path = p_to_path.plus_file(ol_pth.get_file());
+ FileOrFolder p_item = to_move[i];
+
+ String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/");
+ String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/");
+
+ if (p_item.is_file && FileAccess::exists(new_path)) {
+ return false;
+ } else if (!p_item.is_file && DirAccess::exists(new_path)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overwrite) {
+ if (!overwrite) {
+ to_move_path = p_to_path;
+ bool can_move = _check_existing();
+ if (!can_move) {
+ //ask to do something
+ overwrite_dialog->popup_centered_minsize();
+ overwrite_dialog->grab_focus();
+ return;
+ }
+ }
Map<String, String> file_renames;
Map<String, String> folder_renames;
+ bool is_moved = false;
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
String new_path = p_to_path.plus_file(old_path.get_file());
- _try_move_item(to_move[i], new_path, file_renames, folder_renames);
+ if (old_path != new_path) {
+ _try_move_item(to_move[i], new_path, file_renames, folder_renames);
+ is_moved = true;
+ }
}
- _update_dependencies_after_move(file_renames);
- _update_resource_paths_after_move(file_renames);
- _update_favorite_dirs_list_after_move(folder_renames);
+ if (is_moved) {
+ _update_dependencies_after_move(file_renames);
+ _update_resource_paths_after_move(file_renames);
+ _update_project_settings_after_move(file_renames);
+ _update_favorite_dirs_list_after_move(folder_renames);
- print_line("call rescan!");
- _rescan();
+ print_line("call rescan!");
+ _rescan();
+ }
}
void FileSystemDock::_file_option(int p_option) {
@@ -1779,6 +1845,7 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_tree"), &FileSystemDock::_update_tree);
ClassDB::bind_method(D_METHOD("_rescan"), &FileSystemDock::_rescan);
ClassDB::bind_method(D_METHOD("_favorites_pressed"), &FileSystemDock::_favorites_pressed);
+ ClassDB::bind_method(D_METHOD("_show_current_scene_file"), &FileSystemDock::_show_current_scene_file);
//ClassDB::bind_method(D_METHOD("_instance_pressed"),&ScenesDock::_instance_pressed);
ClassDB::bind_method(D_METHOD("_go_to_file_list"), &FileSystemDock::_go_to_file_list);
ClassDB::bind_method(D_METHOD("_dir_rmb_pressed"), &FileSystemDock::_dir_rmb_pressed);
@@ -1795,7 +1862,8 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option);
ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option);
ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm);
- ClassDB::bind_method(D_METHOD("_move_operation_confirm"), &FileSystemDock::_move_operation_confirm);
+ ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_move_with_overwrite"), &FileSystemDock::_move_with_overwrite);
ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm);
ClassDB::bind_method(D_METHOD("_duplicate_operation_confirm"), &FileSystemDock::_duplicate_operation_confirm);
@@ -1828,6 +1896,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
ED_SHORTCUT("filesystem_dock/rename", TTR("Rename"));
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
+ toolbar_hbc->add_constant_override("separation", 0);
add_child(toolbar_hbc);
button_hist_prev = memnew(ToolButton);
@@ -1864,6 +1933,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
button_favorite->set_focus_mode(FOCUS_NONE);
toolbar_hbc->add_child(button_favorite);
+ button_show = memnew(Button);
+ button_show->set_flat(true);
+ button_show->connect("pressed", this, "_show_current_scene_file");
+ toolbar_hbc->add_child(button_show);
+ button_show->set_focus_mode(FOCUS_NONE);
+ button_show->set_tooltip(TTR("Show current scene file."));
+
//Control *spacer = memnew( Control);
/*
@@ -1979,6 +2055,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
rename_dialog->register_text_enter(rename_dialog_text);
rename_dialog->connect("confirmed", this, "_rename_operation_confirm");
+ overwrite_dialog = memnew(ConfirmationDialog);
+ overwrite_dialog->set_text(TTR("There is already file or folder with the same name in this location."));
+ overwrite_dialog->get_ok()->set_text(TTR("Overwrite"));
+ add_child(overwrite_dialog);
+ overwrite_dialog->connect("confirmed", this, "_move_with_overwrite");
+
duplicate_dialog = memnew(ConfirmationDialog);
VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer);
duplicate_dialog->add_child(duplicate_dialog_vb);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index e59d4c96e1..e8ab803cca 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -106,6 +106,7 @@ private:
Button *button_display_mode;
Button *button_hist_next;
Button *button_hist_prev;
+ Button *button_show;
LineEdit *current_path;
LineEdit *search_box;
TextureRect *search_icon;
@@ -128,6 +129,7 @@ private:
LineEdit *duplicate_dialog_text;
ConfirmationDialog *make_dir_dialog;
LineEdit *make_dir_dialog_text;
+ ConfirmationDialog *overwrite_dialog;
ScriptCreateDialog *make_script_dialog_text;
class FileOrFolder {
@@ -145,6 +147,7 @@ private:
FileOrFolder to_rename;
FileOrFolder to_duplicate;
Vector<FileOrFolder> to_move;
+ String to_move_path;
Vector<String> history;
int history_pos;
@@ -186,11 +189,14 @@ private:
void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const;
+ void _update_project_settings_after_move(const Map<String, String> &p_renames) const;
void _make_dir_confirm();
void _rename_operation_confirm();
void _duplicate_operation_confirm();
- void _move_operation_confirm(const String &p_to_path);
+ void _move_with_overwrite();
+ bool _check_existing();
+ void _move_operation_confirm(const String &p_to_path, bool overwrite = false);
void _file_option(int p_option);
void _folder_option(int p_option);
@@ -204,6 +210,7 @@ private:
void _rescan();
void _favorites_pressed();
+ void _show_current_scene_file();
void _search_changed(const String &p_text);
void _dir_rmb_pressed(const Vector2 &p_pos);
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index e42f9780a6..2bfd2eb5c3 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -444,6 +444,7 @@ GroupDialog::GroupDialog() {
set_title("Group Editor");
get_cancel()->hide();
set_as_toplevel(true);
+ set_resizable(true);
error = memnew(ConfirmationDialog);
add_child(error);
diff --git a/editor/icons/README.md b/editor/icons/README.md
index f3aaa23666..3a2aba5b07 100644
--- a/editor/icons/README.md
+++ b/editor/icons/README.md
@@ -2,11 +2,11 @@ The icons here are optimized SVGs, because the editor renders the svgs at runtim
to be small in size, so they can be efficiently parsed.
The original icons can be found at:
-https://github.com/djrm/godot-design/tree/master/assets/icons
+https://github.com/godotengine/godot-design/tree/master/engine/icons
There you can find the optimizer script.
If you add a new icon, please make a pull request to this repo:
-https://github.com/djrm/godot-design/
+https://github.com/godotengine/godot-design/
and store the the optimized SVG version here.
diff --git a/editor/icons/icon_GUI_tree_arrow_up.svg b/editor/icons/icon_GUI_tree_arrow_up.svg
new file mode 100644
index 0000000000..4e6e8e9e29
--- /dev/null
+++ b/editor/icons/icon_GUI_tree_arrow_up.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="12"
+ height="12"
+ version="1.1"
+ viewBox="0 0 12 12"
+ id="svg6"
+ sodipodi:docname="icon_GUI_tree_arrow_up.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1673"
+ inkscape:window-height="594"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="19.666667"
+ inkscape:cx="-4.3220338"
+ inkscape:cy="6.0000001"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="rotate(180,6,526.08476)"
+ id="g4">
+ <path
+ d="m 3,1045.4 3,3 3,-3"
+ id="path2"
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.39216003" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_animated_texture.svg b/editor/icons/icon_animated_texture.svg
new file mode 100644
index 0000000000..dd039df6a7
--- /dev/null
+++ b/editor/icons/icon_animated_texture.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_animated_texture.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10">
+ <filter
+ inkscape:collect="always"
+ style="color-interpolation-filters:sRGB"
+ id="filter822"
+ x="-0.012"
+ width="1.024"
+ y="-0.012"
+ height="1.024">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.07"
+ id="feGaussianBlur824" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <path
+ d="m1 1037.4v14h1.1667v-2h1.8333v2h8v-2h2v2h1v-14h-1v2h-2v-2h-8v2h-1.8333v-2zm1.1667 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2zm-9.8333 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2z"
+ fill="#cea4f1"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:1;filter:url(#filter822)" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_animation_tree.svg b/editor/icons/icon_animation_tree.svg
new file mode 100644
index 0000000000..046506fa37
--- /dev/null
+++ b/editor/icons/icon_animation_tree.svg
@@ -0,0 +1,5 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m1 1v14h1.166v-2h1.834v2h8v-2h2v2h1v-14h-1v2h-2v-2h-8v2h-1.834v-2h-1.166zm4 3h2v1 1h1 3v2h-2v1 1h1 1v2h-1-2a1.0001 1.0001 0 0 1 -1 -1v-1-2h-1a1.0001 1.0001 0 0 1 -1 -1v-1-1-1zm-2.834 1h1.834v2h-1.834v-2zm9.834 0h2v2h-2v-2zm-9.834 4h1.834v2h-1.834v-2zm9.834 0h2v2h-2v-2z" fill="#cea4f1"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_auto_end.svg b/editor/icons/icon_auto_end.svg
new file mode 100644
index 0000000000..9e779c69f4
--- /dev/null
+++ b/editor/icons/icon_auto_end.svg
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_auto_end.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1273"
+ inkscape:window-height="766"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="41.7193"
+ inkscape:cx="12.08616"
+ inkscape:cy="6.9898672"
+ inkscape:window-x="539"
+ inkscape:window-y="208"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2"
+ style="color:#000000;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ d="m 13.999798,14 c 0.552262,-5.5e-5 0.999945,-0.447738 1,-1 V 3 c -5.5e-5,-0.5522619 -0.447738,-0.9999448 -1,-1 H 5.9997976 C 5.6959349,1.9998247 5.4084731,2.1378063 5.2185476,2.375 l -4,5 c -0.29139692,0.3649711 -0.29139692,0.8830289 0,1.248 l 4,5 c 0.189538,0.237924 0.4770584,0.376652 0.78125,0.37695 h 8.0000004 z m -1,-2 H 6.4802976 l -3.1992,-4 3.1992,-4 H 12.999798 Z M 6.9997976,10 V 6 l -2,2 z"
+ sodipodi:nodetypes="cccccccccccccccccccccc" />
+ <g
+ aria-label="E"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;fill:#e0e0e0;fill-opacity:1;stroke:none"
+ id="text829"
+ transform="matrix(0.20475474,0,0,0.20475474,4.7903856,12.365563)">
+ <path
+ d="M 15.129502,-36.414393 H 35.422471 V -30.7308 H 22.649034 v 5.429688 h 12.011718 v 5.683594 H 22.649034 v 6.679687 h 13.203125 v 5.6835938 H 15.129502 Z"
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="path831"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_c_p_u_particles.svg b/editor/icons/icon_c_p_u_particles.svg
new file mode 100644
index 0000000000..00d79cafd2
--- /dev/null
+++ b/editor/icons/icon_c_p_u_particles.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_c_p_u_particles.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1741"
+ inkscape:window-height="753"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8.1355932"
+ inkscape:cy="7.7288136"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#fc9c9c;fill-opacity:0.99607843"
+ d="m 4.5587261,0.60940813 c -0.4226244,0 -0.7617187,0.3410473 -0.7617187,0.76367177 v 0.5078126 c 0,0.1028478 0.020058,0.199689 0.056641,0.2890624 H 2.6602887 c -0.4226245,0 -0.7617188,0.3390944 -0.7617188,0.7617188 v 0.921875 C 1.8581419,3.8469787 1.821771,3.8301112 1.7794293,3.8301112 H 1.2716168 c -0.42262448,0 -0.76367188,0.3410475 -0.76367188,0.7636719 v 0.3730468 c 0,0.4226245 0.3410474,0.7617188 0.76367188,0.7617188 h 0.5078125 c 0.042396,0 0.078663,-0.016851 0.1191406,-0.023437 v 4.4531248 c -0.040428,-0.0066 -0.076799,-0.02344 -0.1191406,-0.02344 H 1.2716168 c -0.42262448,0 -0.76367188,0.341047 -0.76367188,0.763672 v 0.373047 c 0,0.422625 0.3410474,0.761718 0.76367188,0.761718 h 0.5078125 c 0.042396,0 0.078663,-0.01685 0.1191406,-0.02344 v 1.125 c 0,0.422624 0.3390944,0.763672 0.7617188,0.763672 h 1.1367187 v 0.457031 c 0,0.422624 0.3390943,0.763672 0.7617187,0.763672 H 4.931773 c 0.4226244,0 0.7636719,-0.341048 0.7636719,-0.763672 v -0.457031 h 4.4062501 v 0.457031 c 0,0.422624 0.339094,0.763672 0.761719,0.763672 h 0.373047 c 0.422624,0 0.763671,-0.341048 0.763671,-0.763672 v -0.457031 h 1.269532 c 0.422625,0 0.763672,-0.341048 0.763672,-0.763672 v -1.111328 c 0.01774,0.0012 0.03272,0.0098 0.05078,0.0098 h 0.507812 c 0.422624,0 0.763672,-0.339093 0.763672,-0.761718 v -0.373047 c 0,-0.422624 -0.341048,-0.763672 -0.763672,-0.763672 h -0.507812 c -0.01803,0 -0.03307,0.0085 -0.05078,0.0098 V 5.7187831 c 0.01774,0.00122 0.03272,0.00977 0.05078,0.00977 h 0.507812 c 0.422624,0 0.763672,-0.3390943 0.763672,-0.7617188 V 4.5937831 c 0,-0.4226244 -0.341048,-0.7636719 -0.763672,-0.7636719 h -0.507812 c -0.01803,0 -0.03307,0.00855 -0.05078,0.00977 V 2.9316737 c 0,-0.4226244 -0.341047,-0.7617187 -0.763672,-0.7617188 h -1.328125 c 0.03658,-0.089375 0.05859,-0.1862118 0.05859,-0.2890624 V 1.3730799 c 0,-0.42262437 -0.341047,-0.76367177 -0.763671,-0.76367177 h -0.373047 c -0.422625,0 -0.761719,0.3410474 -0.761719,0.76367177 v 0.5078126 c 0,0.1028478 0.02006,0.1996891 0.05664,0.2890624 H 5.6368511 C 5.6734361,2.08058 5.6954449,1.9837431 5.6954449,1.8808925 V 1.3730799 c 0,-0.42262437 -0.3410475,-0.76367177 -0.7636719,-0.76367177 z M 7.7970074,2.9668299 A 3.279661,3.6440678 0 0 1 11.009898,5.9062831 2.1864407,2.1864407 0 0 1 12.89857,8.0683925 2.1864407,2.1864407 0 0 1 10.71107,10.25394 H 4.8809918 A 2.1864407,2.1864407 0 0 1 2.6954449,8.0683925 2.1864407,2.1864407 0 0 1 4.5802105,5.9043299 3.279661,3.6440678 0 0 1 7.7970074,2.9668299 Z M 4.8809918,10.982455 A 0.72881355,0.72881355 0 0 1 5.6095074,11.710971 0.72881355,0.72881355 0 0 1 4.8809918,12.44144 0.72881355,0.72881355 0 0 1 4.1524761,11.710971 0.72881355,0.72881355 0 0 1 4.8809918,10.982455 Z m 5.8300782,0 A 0.72881355,0.72881355 0 0 1 11.441539,11.710971 0.72881355,0.72881355 0 0 1 10.71107,12.44144 0.72881355,0.72881355 0 0 1 9.9825543,11.710971 0.72881355,0.72881355 0 0 1 10.71107,10.982455 Z M 7.7970074,11.710971 A 0.72881355,0.72881355 0 0 1 8.525523,12.44144 0.72881355,0.72881355 0 0 1 7.7970074,13.169955 0.72881355,0.72881355 0 0 1 7.0684918,12.44144 0.72881355,0.72881355 0 0 1 7.7970074,11.710971 Z"
+ id="rect822"
+ inkscape:connector-curvature="0" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="Layer 1" />
+</svg>
diff --git a/editor/icons/icon_cylinder_shape.svg b/editor/icons/icon_cylinder_shape.svg
new file mode 100644
index 0000000000..abda347ec5
--- /dev/null
+++ b/editor/icons/icon_cylinder_shape.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg">
+<rect fill="#68b6ff" width="13.171325" height="7.6993308" x="0.89037383" y="3.6879442"/>
+<ellipse fill="#a2d2ff" cx="7.4772978" cy="3.7229116" rx="6.5864792" ry="2.820821"/>
+<ellipse fill="#68b6ff" cx="7.4746876" cy="11.34481" rx="6.5864792" ry="2.8208208"/>
+</svg>
diff --git a/editor/icons/icon_expand_bottom_dock.svg b/editor/icons/icon_expand_bottom_dock.svg
new file mode 100644
index 0000000000..5a1760f377
--- /dev/null
+++ b/editor/icons/icon_expand_bottom_dock.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_expand_bottom_dock.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="9.4509357"
+ inkscape:cy="6.016355"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0"
+ d="M 4.2130251,4.516057 0.6774912,8.0515909 H 3.2131761 V 13.025308 H 5.2130155 V 8.0515909 H 7.7487004 L 4.2131665,4.516057 Z"
+ id="path829"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path831"
+ d="M 11.907306,4.6119359 8.3717718,8.1474698 h 2.5356852 v 4.9737172 h 1.999839 V 8.1474698 h 2.535685 L 11.907447,4.6119359 Z"
+ style="fill:#e0e0e0"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect855"
+ width="14"
+ height="1.8305085"
+ x="1.2881356"
+ y="1.3700738" />
+</svg>
diff --git a/editor/icons/icon_g_l_e_s_2.svg b/editor/icons/icon_g_l_e_s_2.svg
new file mode 100644
index 0000000000..efc4f01e4f
--- /dev/null
+++ b/editor/icons/icon_g_l_e_s_2.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_g_l_e_s_2.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1484"
+ inkscape:window-height="697"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="13.520979"
+ inkscape:cx="20.549976"
+ inkscape:cy="7.9399684"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path835"
+ d="m 19.839879,15.499154 c -0.0652,-0.0268 -0.141743,-0.1098 -0.170113,-0.184417 -0.03304,-0.08688 -0.05158,-0.95731 -0.05158,-5.912028 V 3.1830459 l 0.108486,-0.1379162 c 0.150269,-0.1910365 0.41814,-0.1907342 0.568677,6.436e-4 l 0.10899,0.1385579 -0.01358,6.2990785 c -0.01194,6.8660953 -0.0921,5.3381383 -0.0921,5.9327083 -0.106573,0.104434 -0.315006,0.142158 -0.458762,0.08303 z M 5.3808767,14.575188 C 4.5309456,14.518738 3.6260357,14.196602 2.9750499,13.718734 2.5767564,13.42636 2.0035795,12.787236 1.747789,12.350269 1.2385669,11.480363 1.0170768,10.580508 1.0213778,9.399057 1.0293972,7.2009406 1.9726797,5.5285643 3.6891526,4.6693537 4.7813316,4.1226444 6.2246017,4.0371807 7.4330177,4.4476602 8.1309525,4.6847376 8.4685433,4.8972607 9.0207129,5.4471587 9.4063328,5.8311907 9.5338898,6.0004852 9.7108978,6.3631718 9.8335428,6.6144683 9.9681328,6.9987435 10.020175,7.2461971 10.145759,7.8433551 10.170431,7.8289765 9.0218356,7.828057 8.5307356,7.8276009 8.0769363,7.8134035 8.0133918,7.7963663 7.9392662,7.7764919 7.8757344,7.6970176 7.8361313,7.5746239 7.5012661,6.5397183 6.6297764,6.0267536 5.4889128,6.193037 4.244092,6.3744711 3.4980921,7.3344965 3.343357,8.9541432 3.2260083,10.182472 3.5434132,11.329338 4.1781352,11.97041 c 0.46237,0.466997 0.9869175,0.673904 1.7084683,0.673904 1.2025378,0 1.9439704,-0.533034 2.1862936,-1.57178 0.055989,-0.240028 0.059178,-0.324448 0.012859,-0.341503 -0.033838,-0.01246 -0.5090516,-0.02871 -1.0560342,-0.03612 L 6.0352096,10.681458 V 9.8178001 8.9541431 l 1.9890278,-0.014575 c 1.0939663,-0.00802 2.0422396,-0.00163 2.1072756,0.014201 l 0.118246,0.028779 -0.01356,2.6814549 -0.01356,2.681455 -0.7170922,0.01455 c -0.8295927,0.01682 -0.7753286,0.05076 -0.8815155,-0.55106 -0.036825,-0.208719 -0.077853,-0.379487 -0.091164,-0.379487 -0.013311,0 -0.16916,0.135437 -0.3463303,0.300972 -0.3894417,0.363866 -0.8188673,0.600316 -1.3418506,0.738852 -0.4725114,0.125166 -0.8081647,0.149449 -1.4638111,0.10591 z M 32.49721,14.469781 c -0.928547,-0.194854 -1.630354,-0.56605 -2.174913,-1.150343 -0.515384,-0.552992 -0.832054,-1.344249 -0.800629,-2.000518 l 0.01549,-0.323408 1.060826,-0.01418 1.060825,-0.01418 0.05146,0.135352 c 0.0283,0.07444 0.0517,0.198593 0.05197,0.275887 8.54e-4,0.230559 0.229434,0.649361 0.479979,0.879354 0.347226,0.318744 0.735307,0.44853 1.431019,0.478576 1.267096,0.05472 2.007349,-0.393206 1.947849,-1.178652 -0.0456,-0.601928 -0.471503,-0.860841 -2.12876,-1.294103 C 32.881626,10.103917 32.242852,9.9264243 32.07283,9.8691486 30.95902,9.4939337 30.283515,8.9537559 29.97948,8.195172 29.836139,7.8375288 29.784025,7.0484225 29.874852,6.6109088 30.100606,5.5234588 31.071976,4.6456053 32.416011,4.314394 33.01697,4.1662997 34.128873,4.156633 34.77144,4.293917 c 1.67335,0.3575071 2.584333,1.270761 2.774448,2.7813655 0.0543,0.4314615 0.0347,0.4394334 -1.080484,0.4394334 -0.521251,0 -0.9851,-0.023133 -1.038665,-0.051802 C 35.367672,7.4313026 35.307808,7.3078793 35.273143,7.1462409 35.195527,6.7843357 35.099156,6.6147944 34.849667,6.4012402 34.543832,6.1394568 34.14764,6.029069 33.515213,6.0294329 c -0.428465,2.111e-4 -0.570793,0.021517 -0.784491,0.1172346 -0.47592,0.2131691 -0.654939,0.4628549 -0.654939,0.9134748 0,0.5904894 0.225799,0.7059322 2.58195,1.3200619 1.350552,0.3520209 1.903346,0.598685 2.415601,1.0778741 0.591219,0.5530567 0.852295,1.2543747 0.796412,2.1393787 -0.07929,1.255762 -0.891416,2.255747 -2.192274,2.699402 -0.885807,0.302103 -2.21918,0.374602 -3.180262,0.172924 z M 11.476954,14.306572 c -0.0138,-0.03619 -0.019,-2.268126 -0.01158,-4.9598581 l 0.0135,-4.8940567 1.066335,-0.01419 c 0.742348,-0.00988 1.088249,0.00399 1.138458,0.045665 0.06009,0.049873 0.07211,0.7135739 0.07211,3.9791612 v 3.9193056 h 2.293081 c 1.756352,0 2.314103,0.01538 2.382892,0.06567 0.07993,0.05845 0.08822,0.166396 0.07543,0.981428 l -0.01437,0.915757 -3.495384,0.01345 c -2.768549,0.0107 -3.500605,-1.69e-4 -3.520473,-0.05234 z m 10.193414,0.0026 c -0.04842,-0.04842 -0.06297,-1.193838 -0.06236,-4.9074882 4.61e-4,-2.6643823 0.01959,-4.8739347 0.04256,-4.9101166 0.03301,-0.05201 0.813774,-0.062971 3.728627,-0.052342 l 3.686862,0.013441 V 5.3948518 6.337024 l -2.5648,0.026171 -2.5648,0.026172 v 0.9421438 0.9421716 l 2.313597,0.026171 c 1.548367,0.017515 2.332217,0.044804 2.36989,0.082507 0.03673,0.036745 0.05127,0.3461819 0.04183,0.889829 l -0.01446,0.8334926 -2.355428,0.02617 -2.355429,0.02617 v 1.0992 1.099199 l 2.617143,0.0274 c 1.439428,0.01507 2.623562,0.03274 2.63141,0.03926 0.0078,0.0065 0.0078,0.441727 0,0.967118 l -0.01427,0.955257 -3.718613,0.01343 c -2.848812,0.01027 -3.733388,-0.0013 -3.781773,-0.04973 z m 17.753791,-0.378679 c -0.04061,-0.105824 0.0759,-0.828141 0.198829,-1.232689 0.288088,-0.948035 0.88431,-1.590368 2.319422,-2.498804 1.100465,-0.6965999 1.86374,-1.2293374 2.17747,-1.5198007 0.515251,-0.477031 0.731074,-1.0868265 0.620161,-1.7522036 -0.126353,-0.7579473 -0.607483,-1.1395723 -1.436711,-1.1395723 -0.930964,0 -1.401324,0.4507271 -1.481617,1.4197789 l -0.03634,0.4383927 h -1.099202 -1.099196 l -0.01524,-0.3725124 c -0.03408,-0.8332648 0.288934,-1.6827799 0.855164,-2.2490093 0.399774,-0.3997734 1.09283,-0.7574546 1.70958,-0.8822975 0.580047,-0.1174131 1.71432,-0.1077309 2.332892,0.019914 1.258364,0.2596698 2.203978,1.0560413 2.520675,2.1228587 0.104477,0.3519131 0.117355,0.4871812 0.09657,1.0144101 -0.01959,0.4962935 -0.04847,0.667451 -0.157022,0.9292002 -0.313508,0.7560998 -0.900391,1.3802206 -1.888823,2.0086882 -1.507571,0.958543 -1.915442,1.243322 -2.230808,1.557578 -0.26352,0.262604 -0.32016,0.345357 -0.261709,0.382352 0.04123,0.0261 1.061246,0.04757 2.280484,0.04802 1.96272,7.11e-4 2.209393,0.0099 2.237659,0.0836 0.01749,0.04554 0.03178,0.408703 0.03178,0.807033 0,0.398331 -0.0143,0.761495 -0.03178,0.807033 -0.0286,0.07445 -0.414152,0.0828 -3.822672,0.0828 -3.236429,0 -3.795092,-0.01093 -3.819578,-0.07475 z"
+ style="fill:#5586a4;fill-opacity:1;stroke-width:0.05234285"
+ sodipodi:nodetypes="ccscccccccccsscsscccccscccsccsccccccccccssscccccccccccccccscsccsccccsssscscccccccscscscccccccscccccccscccccccscccccccccccscccccsssccccccccscscc" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/editor/icons/icon_g_l_e_s_3.svg b/editor/icons/icon_g_l_e_s_3.svg
new file mode 100644
index 0000000000..dfa3c26b38
--- /dev/null
+++ b/editor/icons/icon_g_l_e_s_3.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_g_l_e_s_3.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1484"
+ inkscape:window-height="697"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="13.520979"
+ inkscape:cx="20.549976"
+ inkscape:cy="7.9399684"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ style="fill:#6aa455;fill-opacity:1;stroke-width:0.05234285"
+ d="M 20.011719 2.9023438 C 19.90715 2.9022255 19.801697 2.9494036 19.726562 3.0449219 L 19.619141 3.1835938 L 19.619141 9.4023438 C 19.619141 14.357062 19.636882 15.227573 19.669922 15.314453 C 19.698292 15.38907 19.774644 15.4732 19.839844 15.5 C 19.9836 15.559128 20.192255 15.52045 20.298828 15.416016 C 20.298828 14.821446 20.378685 16.35047 20.390625 9.484375 L 20.404297 3.1835938 L 20.294922 3.0449219 C 20.219653 2.949233 20.116287 2.902462 20.011719 2.9023438 z M 33.578125 4.1972656 C 33.144709 4.2010336 32.716495 4.240406 32.416016 4.3144531 C 31.071981 4.6456644 30.100754 5.5238781 29.875 6.6113281 C 29.784173 7.0488418 29.835175 7.8376693 29.978516 8.1953125 C 30.282551 8.9538964 30.958456 9.4939257 32.072266 9.8691406 C 32.242288 9.9264163 32.881487 10.104023 33.492188 10.263672 C 35.149445 10.696934 35.575494 10.956666 35.621094 11.558594 C 35.680594 12.34404 34.940924 12.791048 33.673828 12.736328 C 32.978116 12.706282 32.589413 12.576557 32.242188 12.257812 C 31.991643 12.02782 31.762573 11.609465 31.761719 11.378906 C 31.761449 11.301612 31.739238 11.176002 31.710938 11.101562 L 31.658203 10.966797 L 30.597656 10.980469 L 29.537109 10.996094 L 29.521484 11.318359 C 29.490059 11.974628 29.806882 12.767321 30.322266 13.320312 C 30.866825 13.904606 31.5695 14.275849 32.498047 14.470703 C 33.459129 14.672381 34.791927 14.598978 35.677734 14.296875 C 36.978592 13.85322 37.789851 12.853418 37.869141 11.597656 C 37.925024 10.712652 37.665438 10.012041 37.074219 9.4589844 C 36.561964 8.9797953 36.008755 8.7328803 34.658203 8.3808594 C 32.302052 7.7667297 32.076172 7.6510363 32.076172 7.0605469 C 32.076172 6.609927 32.254549 6.3596535 32.730469 6.1464844 C 32.944167 6.0507668 33.08716 6.029508 33.515625 6.0292969 C 34.148052 6.028933 34.543774 6.1386072 34.849609 6.4003906 C 35.099098 6.6139448 35.195822 6.7845792 35.273438 7.1464844 C 35.308103 7.3081228 35.366714 7.4312793 35.425781 7.4628906 C 35.479346 7.4915596 35.943593 7.515625 36.464844 7.515625 C 37.580028 7.515625 37.599222 7.5076334 37.544922 7.0761719 C 37.354807 5.5655674 36.444834 4.6504759 34.771484 4.2929688 C 34.450201 4.2243268 34.011541 4.1934977 33.578125 4.1972656 z M 5.5175781 4.1992188 C 4.8691862 4.2376134 4.2355426 4.3965672 3.6894531 4.6699219 C 1.9729802 5.5291325 1.0295038 7.2003211 1.0214844 9.3984375 C 1.0171834 10.579889 1.2388248 11.479703 1.7480469 12.349609 C 2.0038374 12.786576 2.5763159 13.426376 2.9746094 13.71875 C 3.6255952 14.196618 4.5309283 14.517769 5.3808594 14.574219 C 6.0365058 14.617758 6.3712386 14.593916 6.84375 14.46875 C 7.3667333 14.330214 7.7980583 14.094335 8.1875 13.730469 C 8.3646703 13.564934 8.5198921 13.429688 8.5332031 13.429688 C 8.5465141 13.429688 8.588175 13.599875 8.625 13.808594 C 8.7311869 14.410414 8.6762667 14.376195 9.5058594 14.359375 L 10.222656 14.345703 L 10.236328 11.664062 L 10.25 8.9824219 L 10.130859 8.953125 C 10.065823 8.937294 9.1174038 8.9314331 8.0234375 8.9394531 L 6.0351562 8.9550781 L 6.0351562 9.8183594 L 6.0351562 10.681641 L 7.0292969 10.695312 C 7.5762795 10.702722 8.0520995 10.718009 8.0859375 10.730469 C 8.1322565 10.747524 8.1282546 10.832238 8.0722656 11.072266 C 7.8299424 12.111012 7.0892565 12.644531 5.8867188 12.644531 C 5.1651679 12.644531 4.6401044 12.4377 4.1777344 11.970703 C 3.5430124 11.329631 3.2264013 10.183407 3.34375 8.9550781 C 3.4984851 7.3354314 4.2434605 6.3747935 5.4882812 6.1933594 C 6.6291449 6.027076 7.5010723 6.5393131 7.8359375 7.5742188 C 7.8755406 7.6966124 7.9395463 7.7770006 8.0136719 7.796875 C 8.0772164 7.8139122 8.5303844 7.8276689 9.0214844 7.828125 C 10.17008 7.8290445 10.145115 7.8432518 10.019531 7.2460938 C 9.967489 6.9986401 9.8335825 6.6145778 9.7109375 6.3632812 C 9.5339295 6.0005947 9.4071043 5.8312976 9.0214844 5.4472656 C 8.4693148 4.8973676 8.1315285 4.684343 7.4335938 4.4472656 C 6.8293858 4.2420259 6.16597 4.1608241 5.5175781 4.1992188 z M 42.03125 4.2617188 L 41.537109 4.4335938 C 40.933232 4.6433398 40.657695 4.8014669 40.300781 5.1386719 C 39.969225 5.4519119 39.761404 5.8046136 39.621094 6.2910156 C 39.502474 6.7023596 39.433137 7.3494687 39.498047 7.4492188 C 39.531044 7.4999487 39.783863 7.5127831 40.576172 7.5019531 L 41.611328 7.4863281 L 41.691406 7.0703125 C 41.808812 6.4678105 41.927622 6.2685471 42.265625 6.0957031 C 42.510424 5.9705181 42.604184 5.953125 43.019531 5.953125 C 43.426321 5.953125 43.533311 5.9721266 43.765625 6.0878906 C 44.253715 6.3311276 44.455638 6.904517 44.273438 7.53125 C 44.105442 8.109131 43.697334 8.363965 42.791016 8.453125 C 42.521874 8.479605 42.288464 8.51424 42.271484 8.53125 C 42.225224 8.577174 42.232777 9.7874244 42.279297 9.8339844 C 42.301291 9.8559744 42.598053 9.8907794 42.939453 9.9121094 C 43.836652 9.9681724 44.239534 10.166191 44.525391 10.691406 C 44.916028 11.409137 44.561069 12.318315 43.787109 12.582031 C 43.476088 12.688024 42.767292 12.688624 42.470703 12.583984 C 42.00204 12.418633 41.795632 12.174325 41.642578 11.597656 L 41.560547 11.285156 L 40.46875 11.285156 L 39.376953 11.285156 L 39.361328 11.527344 C 39.352678 11.660649 39.384791 11.918152 39.431641 12.099609 C 39.739925 13.294376 40.783209 14.156157 42.212891 14.396484 C 42.284425 14.408514 42.682741 14.422181 43.097656 14.425781 C 44.074936 14.434074 44.653306 14.320796 45.308594 13.996094 C 46.07786 13.61492 46.610204 13.058412 46.847656 12.382812 C 47.087412 11.700564 47.08166 10.999125 46.833984 10.333984 C 46.695621 9.962377 46.130198 9.3782416 45.6875 9.1503906 C 45.523031 9.0657476 45.386773 8.9810006 45.386719 8.9628906 C 45.386654 8.9447846 45.539488 8.8195027 45.724609 8.6835938 C 46.129744 8.3861558 46.390215 8.064434 46.53125 7.6875 C 46.963216 6.532963 46.370297 5.2063894 45.166016 4.6308594 C 44.482944 4.3044164 44.23589 4.2611938 43.072266 4.2617188 L 42.03125 4.2617188 z M 12.544922 4.4375 L 11.478516 4.453125 L 11.464844 9.3476562 C 11.457424 12.039388 11.462763 14.270451 11.476562 14.306641 C 11.49643 14.358812 12.229498 14.370075 14.998047 14.359375 L 18.492188 14.345703 L 18.507812 13.429688 C 18.520602 12.614656 18.511571 12.507669 18.431641 12.449219 C 18.362852 12.398929 17.80518 12.382812 16.048828 12.382812 L 13.755859 12.382812 L 13.755859 8.4628906 C 13.755859 5.1973033 13.743684 4.534248 13.683594 4.484375 C 13.633385 4.4427 13.28727 4.42762 12.544922 4.4375 z M 25.378906 4.4394531 C 22.464053 4.4288241 21.683401 4.4401775 21.650391 4.4921875 C 21.627421 4.5283694 21.607883 6.7379615 21.607422 9.4023438 C 21.606812 13.115994 21.621502 14.260174 21.669922 14.308594 C 21.718307 14.357024 22.60236 14.369645 25.451172 14.359375 L 29.169922 14.345703 L 29.185547 13.390625 C 29.193347 12.865234 29.193347 12.430328 29.185547 12.423828 C 29.177699 12.417308 27.992162 12.399836 26.552734 12.384766 L 23.935547 12.355469 L 23.935547 11.257812 L 23.935547 10.158203 L 26.291016 10.132812 L 28.646484 10.105469 L 28.662109 9.2714844 C 28.671549 8.7278373 28.655871 8.4195575 28.619141 8.3828125 C 28.581468 8.3451095 27.798367 8.3182962 26.25 8.3007812 L 23.935547 8.2734375 L 23.935547 7.3320312 L 23.935547 6.3886719 L 26.501953 6.3632812 L 29.066406 6.3378906 L 29.066406 5.3945312 L 29.066406 4.453125 L 25.378906 4.4394531 z "
+ id="path3424" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/editor/icons/icon_new_root.svg b/editor/icons/icon_new_root.svg
new file mode 100644
index 0000000000..51c79f038d
--- /dev/null
+++ b/editor/icons/icon_new_root.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_new_root.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1474"
+ inkscape:window-height="755"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="9.9306919"
+ inkscape:cy="7.2213369"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <path
+ style="fill:#e0e0e0"
+ d="m 2,4.7813475 v 2.0494746 c -0.6177049,0.3566305 -0.998733,1.0152377 -1,1.7285 0,1.1045694 0.8954305,1.9999999 2,1.9999999 0.7139771,-5.54e-4 1.3735116,-0.381678 1.7305,-0.9999995 h 1.3545593 c 0.3566306,0.6177035 1.0152377,0.9987325 1.7285,0.9999995 1.1045696,0 1.9999996,-0.8954305 1.9999996,-1.9999999 0,-1.1045695 -0.89543,-2 -1.9999996,-2 -0.7139771,5.537e-4 -1.3735116,0.3816774 -1.7305,1 H 4.7285 C 4.5537191,7.2563119 4.3025219,7.0044423 3.99998,6.8288521 V 4.7793775 C 3.4615087,4.8084067 2.7017179,4.8161838 2,4.7813475 Z"
+ id="path2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccscccccc" />
+ <path
+ style="fill:#e0e0e0"
+ d="m 6.8474576,9.6288045 v 1.2020165 c -0.617705,0.35663 -0.998733,1.015237 -1,1.7285 0,1.104569 0.89543,2 2,2 0.713977,-5.54e-4 1.373512,-0.381678 1.7305,-1 h 1.2867634 c 0.35663,0.617704 1.015237,0.998733 1.7285,1 1.104569,0 1.999999,-0.895431 1.999999,-2 0,-1.10457 -0.89543,-2 -1.999999,-2 -0.713977,5.53e-4 -1.373512,0.381677 -1.7305,1 H 9.5759576 c -0.174781,-0.303011 -0.425978,-0.55488 -0.72852,-0.73047 V 9.6268345 c 0,0 -1.264363,0.03681 -1.99998,0.002 z"
+ id="path827"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ inkscape:connector-curvature="0"
+ id="path829"
+ d="m 2.7966098,1.3559322 c -1.104569,0 -2.00000003,0.8954305 -2.00000003,2 5.54e-4,0.7139771 0.38167803,1.3735116 1.00000003,1.7305 0.757716,0.266212 0.949133,0.2840609 1.99998,-0.00197 0.617705,-0.3566306 0.998733,-1.0152377 1,-1.7285 0,-1.1045695 -0.89543,-2 -2,-2 z"
+ style="fill:#84ffb1;fill-opacity:1" />
+</svg>
diff --git a/editor/icons/icon_play_travel.svg b/editor/icons/icon_play_travel.svg
new file mode 100644
index 0000000000..5cd3e07e20
--- /dev/null
+++ b/editor/icons/icon_play_travel.svg
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_play_travel.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1446"
+ inkscape:window-height="646"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8.2818541"
+ inkscape:cy="5.7694884"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="matrix(0.59321602,0,0,0.59321602,-1.2203136,-611.14809)"
+ id="g6">
+ <g
+ id="g4">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <g
+ transform="matrix(0.59321602,0,0,0.59321602,7.5254716,-610.94451)"
+ id="g6-3">
+ <g
+ id="g4-6">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2-7"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect842"
+ width="9.5593224"
+ height="0.54237264"
+ x="3.0058463"
+ y="8.1280737"
+ ry="0.27118632" />
+</svg>
diff --git a/editor/icons/icon_script_create_dialog.svg b/editor/icons/icon_script_create_dialog.svg
new file mode 100644
index 0000000000..27d6c47d49
--- /dev/null
+++ b/editor/icons/icon_script_create_dialog.svg
@@ -0,0 +1,10 @@
+<svg width="17.067" height="17.067" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m6 1v1c-0.55228 0-1 0.44772-1 1v10h-1v-2h-2v2c2.826e-4 0.35698 0.19084 0.68674 0.5 0.86523 0.15194 0.088045 0.32439 0.13452 0.5 0.13477v1h6v-5l3-2v-3h3v-2c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/>
+<path transform="translate(0 1036.4)" d="m6 1c-1.1046 0-2 0.89543-2 2v7h-3v3c0 1.1046 0.89543 2 2 2s2-0.89543 2-2v-10c0-0.55228 0.44772-1 1-1s1 0.44772 1 1v3h5v-1h-4v-2c0-1.1046-0.89543-2-2-2zm-4 10h2v2c0 0.55228-0.44772 1-1 1s-1-0.44772-1-1z" fill="#87e29f"/>
+<circle cx="3" cy="1048.4" r="0" fill="#e0e0e0"/>
+<ellipse cx="12" cy="1048.4" rx=".5" ry="3" fill="#87e29f"/>
+<ellipse transform="rotate(60)" cx="913.91" cy="513.79" rx=".5" ry="3" fill="#87e29f"/>
+<ellipse transform="rotate(120)" cx="901.91" cy="-534.57" rx=".5" ry="3" fill="#87e29f"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_shrink_bottom_dock.svg b/editor/icons/icon_shrink_bottom_dock.svg
new file mode 100644
index 0000000000..c1e8c1bfdb
--- /dev/null
+++ b/editor/icons/icon_shrink_bottom_dock.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_shrink_bottom_dock.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="9.4509357"
+ inkscape:cy="6.016355"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0"
+ d="M 11.907447,9.9752038 15.442981,6.4396699 H 12.907296 V 1.4659528 h -1.999839 l 0,4.9737171 -2.5356852,0 3.5355342,3.5355339 z"
+ id="path829"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path831"
+ d="M 4.2131662,9.8793249 7.7487004,6.343791 H 5.2130152 V 1.3700738 H 3.2131762 V 6.343791 h -2.535685 l 3.535534,3.5355339 z"
+ style="fill:#e0e0e0"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect855"
+ width="14"
+ height="1.8305085"
+ x="-14.832336"
+ y="-13.121187"
+ transform="scale(-1)" />
+</svg>
diff --git a/editor/icons/icon_soft_body.svg b/editor/icons/icon_soft_body.svg
new file mode 100644
index 0000000000..9930026b61
--- /dev/null
+++ b/editor/icons/icon_soft_body.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon_soft_body.svg">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1027"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="18.792233"
+ inkscape:cx="2.8961304"
+ inkscape:cy="4.3816933"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ style="opacity:1;fill:#fc9c9c;fill-opacity:0.99607843;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 2.3447105,1.6091897 c -0.011911,1.9816766 -1.4168958,3.9344766 0,5.9495986 1.4168957,2.0151221 0,6.6693597 0,6.6693597 l 10.9510055,0 c 0,0 1.780829,-4.4523824 0,-6.489075 -1.780829,-2.0366925 -0.183458,-4.119112 0,-6.1298833 z m 1.8894067,0.7549031 7.4390658,0 c -0.431995,1.5996085 -1.62289,4.0426807 0,5.3749802 1.622888,1.3322996 0,5.887932 0,5.887932 l -7.4390658,0 c 0,0 1.3903413,-4.3680495 0,-5.9780743 -1.3903412,-1.6100247 -0.3951213,-3.7149271 0,-5.2848379 z"
+ id="rect4142"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="czcczcccczcczc" />
+</svg>
diff --git a/editor/icons/icon_tool_add_node.svg b/editor/icons/icon_tool_add_node.svg
new file mode 100644
index 0000000000..a4ff4d08a0
--- /dev/null
+++ b/editor/icons/icon_tool_add_node.svg
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_tool_add_node.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1516"
+ inkscape:window-height="747"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <g
+ transform="translate(-26.001 -9.8683)"
+ id="g4">
+ <path
+ style="fill:#e0e0e0;fill-opacity:1"
+ d="m 27.917081,1047.5557 c -0.422624,0 -0.763672,0.3411 -0.763672,0.7637 v 11.8301 c 0,0.4226 0.341048,0.7637 0.763672,0.7637 h 12.507813 c 0.422624,0 0.761719,-0.3411 0.761719,-0.7637 v -11.8301 c 0,-0.4226 -0.339095,-0.7637 -0.761719,-0.7637 z m 1.898438,1.6954 h 8.642578 c 0.422624,0 0.763672,0.341 0.763672,0.7636 v 8.5078 c 0,0.4227 -0.341048,0.7618 -0.763672,0.7618 h -8.642578 c -0.422625,0 -0.763672,-0.3391 -0.763672,-0.7618 v -8.5078 c 0,-0.4226 0.341047,-0.7636 0.763672,-0.7636 z"
+ id="rect821"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect826"
+ width="7.7966104"
+ height="2.3728814"
+ x="30.20439"
+ y="1052.9802"
+ ry="0.76286" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.88253576"
+ id="rect828"
+ width="2.3728814"
+ height="7.5254235"
+ x="32.916256"
+ y="1050.3361"
+ ry="0.72997814" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_tool_connect.svg b/editor/icons/icon_tool_connect.svg
new file mode 100644
index 0000000000..91d5893163
--- /dev/null
+++ b/editor/icons/icon_tool_connect.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_tool_connect.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1516"
+ inkscape:window-height="747"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="3.6875"
+ inkscape:cx="8.5909556"
+ inkscape:cy="7.8012075"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <g
+ transform="translate(-26.001 -9.8683)"
+ id="g4">
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect849"
+ width="14.305085"
+ height="2.1694915"
+ x="26.766621"
+ y="1053.1389"
+ ry="0.76286" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.16725671px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 30.596131,1046.927 v 14.8861 l 8.228847,-7.5722 z"
+ id="path853"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_transition_end.svg b/editor/icons/icon_transition_end.svg
new file mode 100644
index 0000000000..8a1937670a
--- /dev/null
+++ b/editor/icons/icon_transition_end.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_end.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="10.204146"
+ inkscape:cy="5.3391396"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="translate(-2,-1036.4)"
+ id="g6">
+ <g
+ id="g4">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east_asian:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect862"
+ width="3.0681243"
+ height="10.067283"
+ x="11.16989"
+ y="3.0084109"
+ ry="0.76286" />
+</svg>
diff --git a/editor/icons/icon_transition_end_auto.svg b/editor/icons/icon_transition_end_auto.svg
new file mode 100644
index 0000000000..18927bc4ef
--- /dev/null
+++ b/editor/icons/icon_transition_end_auto.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_automatic.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="0.56831798"
+ inkscape:cy="5.1473818"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="translate(-2,-1036.4)"
+ id="g6"
+ style="fill:#77ce57;fill-opacity:1">
+ <g
+ id="g4"
+ style="fill:#77ce57;fill-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east_asian:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto;fill-opacity:1"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#77ce57;fill-opacity:1"
+ id="rect862"
+ width="3.0681243"
+ height="10.067283"
+ x="11.16989"
+ y="3.0084109"
+ ry="0.76286" />
+</svg>
diff --git a/editor/icons/icon_transition_end_auto_big.svg b/editor/icons/icon_transition_end_auto_big.svg
new file mode 100644
index 0000000000..aaedafaf04
--- /dev/null
+++ b/editor/icons/icon_transition_end_auto_big.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_automatic_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="0.3064671"
+ inkscape:cy="14.348448"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="matrix(1.4099529,0,0,1.4099529,-4.1975887,-1462.5094)"
+ id="g6"
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-opacity:1">
+ <g
+ id="g4"
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-opacity:1;fill-rule:evenodd;stroke:#41562e;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-width:1.409953;stroke-opacity:1"
+ id="rect862"
+ width="4.3259106"
+ height="14.194397"
+ x="14.371336"
+ y="3.0076122"
+ ry="1.0755967" />
+</svg>
diff --git a/editor/icons/icon_transition_end_big.svg b/editor/icons/icon_transition_end_big.svg
new file mode 100644
index 0000000000..46d42e95e3
--- /dev/null
+++ b/editor/icons/icon_transition_end_big.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_end_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="-1.1122019"
+ inkscape:cy="10.839132"
+ inkscape:window-x="517"
+ inkscape:window-y="261"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="matrix(1.4203458,0,0,1.4203458,-4.29479,-1473.1325)"
+ id="g6"
+ style="stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g4"
+ style="stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1;stroke:#424242;stroke-width:1.42026603;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect862"
+ width="4.3577976"
+ height="14.299023"
+ x="14.411009"
+ y="3.1868868"
+ ry="1.0835251" />
+</svg>
diff --git a/editor/icons/icon_transition_immediate.svg b/editor/icons/icon_transition_immediate.svg
new file mode 100644
index 0000000000..ba16a33c91
--- /dev/null
+++ b/editor/icons/icon_transition_immediate.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_immediate.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="4.0199579"
+ inkscape:cy="5.3391396"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(-2,-1036.4)"
+ id="g6">
+ <g
+ id="g4">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east_asian:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto;fill-opacity:1"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_transition_immediate_auto.svg b/editor/icons/icon_transition_immediate_auto.svg
new file mode 100644
index 0000000000..c127560145
--- /dev/null
+++ b/editor/icons/icon_transition_immediate_auto.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_immediate_auto.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="-9.2592678"
+ inkscape:cy="5.3391396"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(-2,-1036.4)"
+ id="g6">
+ <g
+ id="g4">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east_asian:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto;fill-opacity:1"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_transition_immediate_auto_big.svg b/editor/icons/icon_transition_immediate_auto_big.svg
new file mode 100644
index 0000000000..80d35a36f3
--- /dev/null
+++ b/editor/icons/icon_transition_immediate_auto_big.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_immediate_auto_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="11.321237"
+ inkscape:cy="3.5752171"
+ inkscape:window-x="517"
+ inkscape:window-y="261"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="matrix(1.571031,0,0,1.571031,-2.7257681,-1630.6239)"
+ id="g6"
+ style="stroke:#404040;stroke-opacity:1">
+ <g
+ id="g4"
+ style="stroke:#404040;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-rule:evenodd;stroke:#41562e;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;fill-opacity:1"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_transition_immediate_big.svg b/editor/icons/icon_transition_immediate_big.svg
new file mode 100644
index 0000000000..108dcdd500
--- /dev/null
+++ b/editor/icons/icon_transition_immediate_big.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_immediate_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="0.19928629"
+ inkscape:cy="4.534006"
+ inkscape:window-x="517"
+ inkscape:window-y="261"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="matrix(1.571031,0,0,1.571031,-2.7257681,-1630.6239)"
+ id="g6"
+ style="stroke:#404040;stroke-opacity:1">
+ <g
+ id="g4"
+ style="stroke:#404040;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;stroke:#404040;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;fill-opacity:1"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/icons/icon_transition_sync.svg b/editor/icons/icon_transition_sync.svg
new file mode 100644
index 0000000000..267d806615
--- /dev/null
+++ b/editor/icons/icon_transition_sync.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_sync.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="10.204146"
+ inkscape:cy="5.3391396"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="translate(2.5542471,-1036.4)"
+ id="g6">
+ <g
+ id="g4">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect862"
+ width="3.0681243"
+ height="10.067283"
+ x="1.9655174"
+ y="3.0084109"
+ ry="0.76286" />
+</svg>
diff --git a/editor/icons/icon_transition_sync_auto.svg b/editor/icons/icon_transition_sync_auto.svg
new file mode 100644
index 0000000000..5ce61e3a6a
--- /dev/null
+++ b/editor/icons/icon_transition_sync_auto.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg8"
+ sodipodi:docname="icon_transition_sync_auto.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="0.56831798"
+ inkscape:cy="5.1473818"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="translate(3.0815809,-1036.4)"
+ id="g6"
+ style="fill:#77ce57;fill-opacity:1">
+ <g
+ id="g4"
+ style="fill:#77ce57;fill-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-opacity:1;fill-rule:evenodd;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#77ce57;fill-opacity:1"
+ id="rect862"
+ width="3.0681243"
+ height="10.067283"
+ x="1.9655174"
+ y="3.0084109"
+ ry="0.76286" />
+</svg>
diff --git a/editor/icons/icon_transition_sync_auto_big.svg b/editor/icons/icon_transition_sync_auto_big.svg
new file mode 100644
index 0000000000..3e84d76398
--- /dev/null
+++ b/editor/icons/icon_transition_sync_auto_big.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_sync_auto_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="0.3064671"
+ inkscape:cy="14.348448"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="matrix(1.4099529,0,0,1.4099529,2.1752927,-1462.5094)"
+ id="g6"
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-opacity:1">
+ <g
+ id="g4"
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#77ce57;fill-opacity:1;fill-rule:evenodd;stroke:#41562e;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#77ce57;fill-opacity:1;stroke:#41562e;stroke-width:1.409953;stroke-opacity:1"
+ id="rect862"
+ width="4.3259106"
+ height="14.194397"
+ x="1.6255733"
+ y="3.0076122"
+ ry="1.0755967" />
+</svg>
diff --git a/editor/icons/icon_transition_sync_big.svg b/editor/icons/icon_transition_sync_big.svg
new file mode 100644
index 0000000000..e7cf63e0b3
--- /dev/null
+++ b/editor/icons/icon_transition_sync_big.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20"
+ height="20"
+ version="1.1"
+ viewBox="0 0 20 20"
+ id="svg8"
+ sodipodi:docname="icon_transition_sync_big.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1403"
+ inkscape:window-height="782"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="19.226781"
+ inkscape:cy="9.27981"
+ inkscape:window-x="302"
+ inkscape:window-y="226"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="matrix(1.4203458,0,0,1.4203458,1.8747015,-1473.1325)"
+ id="g6"
+ style="stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g4"
+ style="stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="m 4.9883,1039.4 c -0.5469,0.01 -0.98717,0.4511 -0.98828,0.998 v 8 c 1.163e-4,0.7986 0.89011,1.275 1.5547,0.8321 l 6,-4 c 0.59362,-0.3959 0.59362,-1.2682 0,-1.6641 l -6,-4 c -0.1678,-0.1111 -0.3652,-0.1689 -0.56641,-0.166 z"
+ dominant-baseline="auto"
+ style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;fill-rule:evenodd;stroke:#424242;stroke-width:0.99994373;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1;stroke:#424242;stroke-width:1.42026603;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect862"
+ width="4.3577976"
+ height="14.299023"
+ x="1.4618562"
+ y="3.1868868"
+ ry="1.0835251" />
+</svg>
diff --git a/editor/icons/icon_visual_shader.svg b/editor/icons/icon_visual_shader.svg
new file mode 100644
index 0000000000..e2c4f128b2
--- /dev/null
+++ b/editor/icons/icon_visual_shader.svg
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg20"
+ sodipodi:docname="icon_visual_shader.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata26">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs24" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview22"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg20" />
+ <g
+ id="g18"
+ transform="matrix(1,0,0,0.50605327,0,0.49394673)">
+ <path
+ d="M 2,1 C 1.44774,1.0001 1.00006,1.4477 1,2 v 12 c 5.52e-5,0.5523 0.44774,0.9999 1,1 h 12 c 0.55226,-10e-5 0.99994,-0.4477 1,-1 V 6 L 10,1 Z m 1,2 h 6 v 3 c 0,0.554 0.44599,1 1,1 h 3 v 6 H 3 Z"
+ id="path2"
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0" />
+ <path
+ d="m 10,11 h 2 v 1 h -2 z"
+ id="path4"
+ inkscape:connector-curvature="0"
+ style="fill:#9f70ff" />
+ <path
+ d="M 4,6 H 6 V 7 H 4 Z"
+ id="path6"
+ inkscape:connector-curvature="0"
+ style="fill:#ffeb70" />
+ <path
+ d="m 8,8 h 4 V 9 H 8 Z"
+ id="path8"
+ inkscape:connector-curvature="0"
+ style="fill:#9dff70" />
+ <path
+ d="M 7,6 H 8 V 7 H 7 Z"
+ id="path10"
+ inkscape:connector-curvature="0"
+ style="fill:#70deff" />
+ <path
+ d="m 4,11 h 5 v 1 H 4 Z"
+ id="path12"
+ inkscape:connector-curvature="0"
+ style="fill:#ff70ac" />
+ <path
+ d="M 4,4 H 7 V 5 H 4 Z"
+ id="path14"
+ inkscape:connector-curvature="0"
+ style="fill:#ff7070" />
+ <path
+ d="M 4,8 H 7 V 9 H 4 Z"
+ id="path16"
+ inkscape:connector-curvature="0"
+ style="fill:#70ffb9" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0"
+ d="m 2.8642321,9 v 6 h 2 a 3,3 0 0 0 3,-3 V 9 h -2 v 3 a 1,1 0 0 1 -1,1 V 9 Z"
+ id="path1394" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0"
+ d="m 10.864232,9 a 2,2 0 0 0 -1.7323999,1 2,2 0 0 0 0,2 2,2 0 0 0 1.7323999,1 H 8.8642321 v 2 h 1.9999999 a 2,2 0 0 0 1.7324,-1 2,2 0 0 0 0,-2 2,2 0 0 0 -1.7324,-1 h 2 V 9 Z"
+ id="path30" />
+</svg>
diff --git a/editor/icons/icon_vulkan.svg b/editor/icons/icon_vulkan.svg
new file mode 100644
index 0000000000..1d5fed0305
--- /dev/null
+++ b/editor/icons/icon_vulkan.svg
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_vulkan.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="10.24"
+ inkscape:cx="9.4970674"
+ inkscape:cy="11.192118"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g8" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+ <g
+ transform="matrix(0.04333868,0,0,0.04333868,-4.0493236,-3.7704963)"
+ id="g8">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 724.1,432.41989 h -40.6 c 0,0 0,-99 0,-129.7 13,7.2 30.1,20.5 40.6,33.3 z"
+ id="path10"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g12"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 381.8,385.1 h -50.6 l -66,-204 h 46 l 45.4,143.5 h 0.6 l 46,-143.5 h 46.3 z"
+ id="path14"
+ style="fill:#e6555a;fill-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ d="M 585.5,385.1 H 546.9 V 364.5 H 546 c -5.1,8.6 -11.8,14.8 -20,18.6 -8.2,3.8 -16.6,5.7 -25.1,5.7 -10.9,0 -19.8,-1.4 -26.7,-4.3 -7,-2.9 -12.4,-6.9 -16.4,-12.1 -4,-5.2 -6.8,-11.6 -8.4,-19.1 -1.6,-7.5 -2.4,-15.9 -2.4,-25 v -90.9 h 40.6 v 83.4 c 0,12.2 1.9,21.3 5.7,27.3 3.8,6 10.6,9 20.3,9 11,0 19.1,-3.3 24,-9.9 5,-6.6 7.4,-17.4 7.4,-32.4 v -77.4 h 40.6 v 147.7 z"
+ id="path16"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ <polygon
+ points="730.8,296.2 730.7,290.5 781.9,237.3 829.9,237.3 774.2,291.6 836.2,385.1 787,385.1 "
+ id="polygon18"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 843.6,330.11989 c 0.6,-9.5 3,-17.4 7.2,-23.7 4.2,-6.3 9.5,-11.3 16,-15.1 6.5,-3.8 13.8,-6.5 21.9,-8.1 8.1,-1.6 16.2,-2.4 24.4,-2.4 7.4,0 15,0.5 22.6,1.6 7.6,1.1 14.6,3.1 20.9,6.1 6.3,3.1 11.4,7.3 15.4,12.7 4,5.4 6,12.6 6,21.6 v 76.9 c 0,6.7 0.4,13.1 1.1,19.1 0.8,6.1 2.1,10.7 4,13.7 h -41.2 c -0.8,-2.3 -1.4,-4.6 -1.9,-7 -0.5,-2.4 -0.8,-4.8 -1,-7.3 -6.5,6.7 -14.1,11.3 -22.9,14 -8.8,2.7 -17.7,4 -26.9,4 -7,0 -13.6,-0.9 -19.7,-2.6 -6.1,-1.7 -11.4,-4.4 -16,-8 -4.6,-3.6 -8.2,-8.2 -10.7,-13.7 -2.6,-5.5 -3.9,-12.1 -3.9,-19.7 0,-8.4 1.5,-15.3 4.4,-20.7 3,-5.4 6.8,-9.8 11.4,-13 4.7,-3.2 10,-5.7 16,-7.3 6,-1.6 12,-2.9 18.1,-3.9 6.1,-0.9 12.1,-1.7 18,-2.3 5.9,-0.6 11.1,-1.4 15.7,-2.6 4.6,-1.1 8.2,-2.8 10.9,-5 2.7,-2.2 3.9,-5.4 3.7,-9.6 0,-4.4 -0.7,-7.9 -2.2,-10.4 -1.4,-2.6 -3.3,-4.6 -5.7,-6 -2.4,-1.4 -5.1,-2.4 -8.3,-2.9 -3.1,-0.5 -6.5,-0.7 -10.1,-0.7 -8,0 -14.3,1.7 -18.9,5.1 -4.6,3.4 -7.2,9.1 -8,17.1 h -40.3 z m 93.8,30 c -1.7,1.5 -3.9,2.7 -6.4,3.6 -2.6,0.9 -5.3,1.6 -8.3,2.2 -2.9,0.6 -6,1 -9.3,1.4 -3.2,0.4 -6.5,0.9 -9.7,1.4 -3,0.6 -6,1.3 -9,2.3 -3,1 -5.5,2.2 -7.7,3.9 -2.2,1.6 -4,3.7 -5.3,6.1 -1.3,2.5 -2,5.6 -2,9.4 0,3.6 0.7,6.7 2,9.1 1.3,2.5 3.1,4.4 5.4,5.9 2.3,1.4 5,2.4 8,3 3.1,0.6 6.2,0.9 9.4,0.9 8,0 14.2,-1.3 18.6,-4 4.4,-2.7 7.6,-5.9 9.7,-9.6 2.1,-3.7 3.4,-7.5 3.9,-11.3 0.5,-3.8 0.7,-6.9 0.7,-9.1 z"
+ id="path20"
+ style="fill:#e6555a;fill-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 1004.2,284.61989 h 38.6 v 20.6 h 0.9 c 5.1,-8.6 11.8,-14.8 20,-18.7 8.2,-3.9 16.6,-5.9 25.1,-5.9 10.9,0 19.8,1.5 26.7,4.4 7,3 12.4,7.1 16.4,12.3 4,5.2 6.8,11.6 8.4,19.1 1.6,7.5 2.4,15.9 2.4,25 v 90.9 h -40.6 v -83.4 c 0,-12.2 -1.9,-21.3 -5.7,-27.3 -3.8,-6 -10.6,-9 -20.3,-9 -11,0 -19,3.3 -24,9.9 -5,6.6 -7.4,17.4 -7.4,32.4 v 77.4 h -40.6 v -147.7 z"
+ id="path22"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g24"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="M 612.4,211.8 V 385 H 653 V 234.2 c -13.1,-8 -26.6,-15.5 -40.6,-22.4 z"
+ id="path26"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ d="m 198.4,266.51989 c 23.5,-68.9 164.2,-94.2 314.1,-56.4 90,22.6 163.5,66.5 211.5,109.9 -21.7,-57.6 -127.3,-139.6 -272.8,-167.7 -164.5,-31.8 -326.7,-3.9 -346.8,69.1 -14.5,52.7 49.2,114.5 147.7,156.7 -44.3,-35.8 -65.8,-76 -53.7,-111.6 z"
+ id="path28"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g30"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="M 724.2,247.6 V 181 h -40.6 v 20.2 c 17.3,15.5 31,31.2 40.6,46.4 z"
+ id="path32"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 2fb3bf7b1e..a13f741ee7 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -1785,8 +1785,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
}
}
- Quat q = xform.basis;
- q.normalize();
+ Quat q = xform.basis.get_rotation_quat();
Vector3 s = xform.basis.get_scale();
Vector3 l = xform.origin;
@@ -1838,8 +1837,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
- Quat q = xform.basis;
- q.normalize();
+ Quat q = xform.basis.get_rotation_quat();
Vector3 s = xform.basis.get_scale();
Vector3 l = xform.origin;
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index 777f757bb4..eb0bc0f782 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -1989,8 +1989,7 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye
int bone = node->joints[i].godot_bone_index;
xform = skeleton->get_bone_rest(bone).affine_inverse() * xform;
- rot = xform.basis;
- rot.normalize();
+ rot = xform.basis.get_rotation_quat();
scale = xform.basis.get_scale();
pos = xform.origin;
}
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index 21803a2184..b8dd4a87b7 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -188,7 +188,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati
return OK;
}
-static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, List<String> *r_missing_deps) {
+static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, List<String> *r_missing_deps) {
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
@@ -200,6 +200,8 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p
bool generate_tangents = p_generate_tangents;
Vector3 scale_mesh = p_scale_mesh;
bool flip_faces = false;
+ int mesh_flags = p_optimize ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
+
//bool flip_faces = p_options["force/flip_faces"];
//bool force_smooth = p_options["force/smooth_shading"];
//bool weld_vertices = p_options["force/weld_vertices"];
@@ -331,7 +333,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p
surf_tool->set_material(material_map[current_material_library][current_material]);
}
- mesh = surf_tool->commit(mesh);
+ mesh = surf_tool->commit(mesh, mesh_flags);
if (current_material != String()) {
mesh->surface_set_name(mesh->get_surface_count() - 1, current_material.get_basename());
@@ -402,7 +404,7 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, in
List<Ref<Mesh> > meshes;
- Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), r_missing_deps);
+ Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), r_missing_deps);
if (err != OK) {
if (r_err) {
@@ -470,6 +472,7 @@ void ResourceImporterOBJ::get_import_options(List<ImportOption> *r_options, int
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true));
}
bool ResourceImporterOBJ::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
@@ -480,7 +483,7 @@ Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_s
List<Ref<Mesh> > meshes;
- Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], NULL);
+ Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], NULL);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index fdbf66f656..a5ad34f377 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -130,7 +130,9 @@ void EditorSceneImporter::_bind_methods() {
/////////////////////////////////
void EditorScenePostImport::_bind_methods() {
- BIND_VMETHOD(MethodInfo("post_import", PropertyInfo(Variant::OBJECT, "scene")));
+ BIND_VMETHOD(MethodInfo(Variant::OBJECT, "post_import", PropertyInfo(Variant::OBJECT, "scene")));
+ ClassDB::bind_method(D_METHOD("get_source_folder"), &EditorScenePostImport::get_source_folder);
+ ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file);
}
Node *EditorScenePostImport::post_import(Node *p_scene) {
@@ -141,6 +143,21 @@ Node *EditorScenePostImport::post_import(Node *p_scene) {
return p_scene;
}
+String EditorScenePostImport::get_source_folder() const {
+
+ return source_folder;
+}
+
+String EditorScenePostImport::get_source_file() const {
+
+ return source_file;
+}
+
+void EditorScenePostImport::init(const String &p_source_folder, const String &p_source_file) {
+ source_folder = p_source_folder;
+ source_file = p_source_file;
+}
+
EditorScenePostImport::EditorScenePostImport() {
}
@@ -224,24 +241,42 @@ String ResourceImporterScene::get_preset_name(int p_idx) const {
static bool _teststr(const String &p_what, const String &p_str) {
- if (p_what.findn("$" + p_str) != -1) //blender and other stuff
+ String what = p_what;
+
+ //remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this
+ while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
+
+ what = what.substr(0, what.length() - 1);
+ }
+
+ if (what.findn("$" + p_str) != -1) //blender and other stuff
return true;
- if (p_what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters
+ if (what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters
return true;
- if (p_what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters
+ if (what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters
return true;
return false;
}
static String _fixstr(const String &p_what, const String &p_str) {
- if (p_what.findn("$" + p_str) != -1) //blender and other stuff
- return p_what.replace("$" + p_str, "");
- if (p_what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters
- return p_what.substr(0, p_what.length() - (p_str.length() + 1));
- if (p_what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters
- return p_what.substr(0, p_what.length() - (p_str.length() + 1));
- return p_what;
+ String what = p_what;
+
+ //remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this
+ while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
+
+ what = what.substr(0, what.length() - 1);
+ }
+
+ String end = p_what.substr(what.length(), p_what.length() - what.length());
+
+ if (what.findn("$" + p_str) != -1) //blender and other stuff
+ return what.replace("$" + p_str, "") + end;
+ if (what.to_lower().ends_with("-" + p_str)) //collada only supports "_" and "-" besides letters
+ return what.substr(0, what.length() - (p_str.length() + 1)) + end;
+ if (what.to_lower().ends_with("_" + p_str)) //collada only supports "_" and "-" besides letters
+ return what.substr(0, what.length() - (p_str.length() + 1)) + end;
+ return what;
}
Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<ArrayMesh>, Ref<Shape> > &collision_map, LightBakeMode p_light_bake_mode) {
@@ -437,13 +472,19 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array
Node *col;
if (_teststr(name, "col")) {
- mi->set_name(_fixstr(name, "col"));
+ String new_name = _fixstr(name, "col");
+ if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) {
+ mi->set_name(new_name);
+ }
col = mi->create_trimesh_collision_node();
ERR_FAIL_COND_V(!col, NULL);
col->set_name("col");
} else {
- mi->set_name(_fixstr(name, "convcol"));
+ String new_name = _fixstr(name, "convcol");
+ if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) {
+ mi->set_name(new_name);
+ }
col = mi->create_convex_collision_node();
ERR_FAIL_COND_V(!col, NULL);
@@ -893,7 +934,6 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String
}
String ext_name = p_base_path.plus_file(_make_extname(E->get()) + ".anim");
-
if (FileAccess::exists(ext_name) && p_keep_animations) {
//try to keep custom animation tracks
Ref<Animation> old_anim = ResourceLoader::load(ext_name, "Animation", true);
@@ -907,6 +947,7 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String
}
}
+ anim->set_path(ext_name, true); //if not set, then its never saved externally
ResourceSaver::save(ext_name, anim, ResourceSaver::FLAG_CHANGE_PATH);
p_animations[anim] = anim;
}
@@ -1346,6 +1387,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
if (post_import_script.is_valid()) {
+ post_import_script->init(base_path, p_source_file);
scene = post_import_script->post_import(scene);
if (!scene) {
EditorNode::add_io_error(TTR("Error running post-import script:") + " " + post_import_script_path);
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 9c3ec7a29b..2bde9432fc 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -75,11 +75,17 @@ class EditorScenePostImport : public Reference {
GDCLASS(EditorScenePostImport, Reference);
+ String source_folder;
+ String source_file;
+
protected:
static void _bind_methods();
public:
+ String get_source_folder() const;
+ String get_source_file() const;
virtual Node *post_import(Node *p_scene);
+ virtual void init(const String &p_scene_folder, const String &p_scene_path);
EditorScenePostImport();
};
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index beaa8d9600..17a9394b51 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -395,6 +395,10 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
}
+
+ if (normal) {
+ image->normalize();
+ }
}
if (fix_alpha_border) {
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
new file mode 100644
index 0000000000..2e128db883
--- /dev/null
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -0,0 +1,741 @@
+#include "animation_blend_space_1d_editor.h"
+
+#include "os/keyboard.h"
+#include "scene/animation/animation_blend_tree.h"
+
+void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) {
+ anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace1D>(p_object));
+}
+
+bool AnimationNodeBlendSpace1DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("AnimationNodeBlendSpace1D");
+}
+
+void AnimationNodeBlendSpace1DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ button->show();
+ editor->make_bottom_panel_item_visible(anim_tree_editor);
+ anim_tree_editor->set_process(true);
+ } else {
+ if (anim_tree_editor->is_visible_in_tree()) {
+ editor->hide_bottom_panel();
+ }
+
+ button->hide();
+ anim_tree_editor->set_process(false);
+ }
+}
+
+AnimationNodeBlendSpace1DEditorPlugin::AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ anim_tree_editor = memnew(AnimationNodeBlendSpace1DEditor);
+ anim_tree_editor->set_custom_minimum_size(Size2(0, 150 * EDSCALE));
+
+ button = editor->add_bottom_panel_item(TTR("BlendSpace1D"), anim_tree_editor);
+ button->hide();
+}
+
+AnimationNodeBlendSpace1DEditorPlugin::~AnimationNodeBlendSpace1DEditorPlugin() {
+}
+
+void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEventKey> k = p_event;
+
+ if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) {
+ if (selected_point != -1) {
+ _erase_selected();
+ accept_event();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) || (mb->get_button_index() == BUTTON_LEFT && tool_create->is_pressed()))) {
+ menu->clear();
+ animations_menu->clear();
+ animations_to_add.clear();
+
+ List<StringName> classes;
+ ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
+ classes.sort_custom<StringName::AlphCompare>();
+
+ menu->add_submenu_item(TTR("Add Animation"), "animations");
+
+ AnimationTree *gp = blend_space->get_tree();
+ ERR_FAIL_COND(!gp);
+
+ if (gp->has_node(gp->get_animation_player())) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player()));
+
+ if (ap) {
+ List<StringName> names;
+ ap->get_animation_list(&names);
+
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ animations_menu->add_icon_item(get_icon("Animation", "Editoricons"), E->get());
+ animations_to_add.push_back(E->get());
+ }
+ }
+ }
+
+ for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
+ String name = String(E->get()).replace_first("AnimationNode", "");
+ if (name == "Animation")
+ continue;
+
+ int idx = menu->get_item_count();
+ menu->add_item(vformat("Add %s", name));
+ menu->set_item_metadata(idx, E->get());
+ }
+
+ menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position()));
+ menu->popup();
+
+ add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x;
+ add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
+ add_point_pos += blend_space->get_min_space();
+
+ if (snap->is_pressed()) {
+ add_point_pos = Math::stepify(add_point_pos, blend_space->get_snap());
+ }
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ blend_space_draw->update(); // why not
+
+ // try to see if a point can be selected
+ selected_point = -1;
+ _update_tool_erase();
+
+ for (int i = 0; i < points.size(); i++) {
+
+ if (Math::abs(float(points[i] - mb->get_position().x)) < 10 * EDSCALE) {
+ selected_point = i;
+
+ Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
+ EditorNode::get_singleton()->push_item(node.ptr(), "", true);
+ dragging_selected_attempt = true;
+ drag_from = mb->get_position();
+ _update_tool_erase();
+ _update_edited_point_pos();
+ return;
+ }
+ }
+ }
+
+ if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT) {
+ if (dragging_selected) {
+ // move
+ float point = blend_space->get_blend_point_position(selected_point);
+ point += drag_ofs.x;
+
+ if (snap->is_pressed()) {
+ point = Math::stepify(point, blend_space->get_snap());
+ }
+
+ updating = true;
+ undo_redo->create_action("Move Node Point");
+ undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
+ undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->add_do_method(this, "_update_edited_point_pos");
+ undo_redo->add_undo_method(this, "_update_edited_point_pos");
+ undo_redo->commit_action();
+ updating = false;
+ _update_edited_point_pos();
+ }
+
+ dragging_selected_attempt = false;
+ dragging_selected = false;
+ blend_space_draw->update();
+ }
+
+ // *set* the blend
+ if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ float blend_pos = mb->get_position().x / blend_space_draw->get_size().x;
+ blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
+ blend_pos += blend_space->get_min_space();
+
+ blend_space->set_blend_pos(blend_pos);
+ blend_space_draw->update();
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid() && !blend_space_draw->has_focus()) {
+ blend_space_draw->grab_focus();
+ blend_space_draw->update();
+ }
+
+ if (mm.is_valid() && dragging_selected_attempt) {
+ dragging_selected = true;
+ drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * ((blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, 0));
+ blend_space_draw->update();
+ _update_edited_point_pos();
+ }
+
+ if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+ float blend_pos = mm->get_position().x / blend_space_draw->get_size().x;
+ blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
+ blend_pos += blend_space->get_min_space();
+
+ blend_space->set_blend_pos(blend_pos);
+ blend_space_draw->update();
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
+
+ Color linecolor = get_color("font_color", "Label");
+ Color linecolor_soft = linecolor;
+ linecolor_soft.a *= 0.5;
+
+ Ref<Font> font = get_font("font", "Label");
+ Ref<Texture> icon = get_icon("KeyValue", "EditorIcons");
+ Ref<Texture> icon_selected = get_icon("KeySelected", "EditorIcons");
+
+ Size2 s = blend_space_draw->get_size();
+
+ if (blend_space_draw->has_focus()) {
+ Color color = get_color("accent_color", "Editor");
+ blend_space_draw->draw_rect(Rect2(Point2(), s), color, false);
+ }
+
+ blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor);
+
+ if (blend_space->get_min_space() < 0) {
+ float point = 0.0;
+ point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
+ point *= s.width;
+
+ float x = point;
+
+ blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor);
+ blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height() + font->get_ascent()), "0", linecolor);
+ blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft);
+ }
+
+ if (snap->is_pressed()) {
+
+ linecolor_soft.a = linecolor.a * 0.1;
+
+ if (blend_space->get_snap() > 0) {
+ int prev_idx = -1;
+
+ for (int i = 0; i < s.x; i++) {
+ float v = blend_space->get_min_space() + i * (blend_space->get_max_space() - blend_space->get_min_space()) / s.x;
+ int idx = int(v / blend_space->get_snap());
+
+ if (i > 0 && prev_idx != idx) {
+ blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft);
+ }
+
+ prev_idx = idx;
+ }
+ }
+ }
+
+ points.clear();
+
+ for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
+ float point = blend_space->get_blend_point_position(i);
+
+ if (dragging_selected && selected_point == i) {
+ point += drag_ofs.x;
+ if (snap->is_pressed()) {
+ point = Math::stepify(point, blend_space->get_snap());
+ }
+ }
+
+ point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
+ point *= s.width;
+
+ points.push_back(point);
+
+ Vector2 gui_point = Vector2(point, s.height / 2.0);
+
+ gui_point -= (icon->get_size() / 2.0);
+
+ gui_point = gui_point.floor();
+
+ if (i == selected_point) {
+ blend_space_draw->draw_texture(icon_selected, gui_point);
+ } else {
+ blend_space_draw->draw_texture(icon, gui_point);
+ }
+ }
+
+ // blend position
+ {
+ Color color;
+ if (tool_blend->is_pressed()) {
+ color = get_color("accent_color", "Editor");
+ } else {
+ color = linecolor;
+ color.a *= 0.5;
+ }
+
+ float point = blend_space->get_blend_pos();
+ point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
+ point *= s.width;
+
+ Vector2 gui_point = Vector2(point, s.height / 2.0);
+
+ float mind = 5 * EDSCALE;
+ float maxd = 15 * EDSCALE;
+ blend_space_draw->draw_line(gui_point + Vector2(mind, 0), gui_point + Vector2(maxd, 0), color, 2);
+ blend_space_draw->draw_line(gui_point + Vector2(-mind, 0), gui_point + Vector2(-maxd, 0), color, 2);
+ blend_space_draw->draw_line(gui_point + Vector2(0, mind), gui_point + Vector2(0, maxd), color, 2);
+ blend_space_draw->draw_line(gui_point + Vector2(0, -mind), gui_point + Vector2(0, -maxd), color, 2);
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_update_space() {
+
+ if (updating)
+ return;
+
+ updating = true;
+
+ if (blend_space->get_parent().is_valid()) {
+ goto_parent_hb->show();
+ } else {
+ goto_parent_hb->hide();
+ }
+
+ max_value->set_value(blend_space->get_max_space());
+ min_value->set_value(blend_space->get_min_space());
+
+ label_value->set_text(blend_space->get_value_label());
+
+ snap_value->set_value(blend_space->get_snap());
+
+ blend_space_draw->update();
+
+ updating = false;
+}
+
+void AnimationNodeBlendSpace1DEditor::_config_changed(double) {
+ if (updating)
+ return;
+
+ updating = true;
+ undo_redo->create_action("Change BlendSpace1D Limits");
+ undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
+ undo_redo->add_do_method(blend_space.ptr(), "set_min_space", min_value->get_value());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());
+ undo_redo->add_do_method(blend_space.ptr(), "set_snap", snap_value->get_value());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_labels_changed(String) {
+ if (updating)
+ return;
+
+ updating = true;
+ undo_redo->create_action("Change BlendSpace1D Labels", UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void AnimationNodeBlendSpace1DEditor::_snap_toggled() {
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
+ String type = menu->get_item_metadata(p_index);
+
+ Object *obj = ClassDB::instance(type);
+ ERR_FAIL_COND(!obj);
+ AnimationNode *an = Object::cast_to<AnimationNode>(obj);
+ ERR_FAIL_COND(!an);
+
+ Ref<AnimationNode> node(an);
+
+ updating = true;
+ undo_redo->create_action("Add Node Point");
+ undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
+ undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) {
+ Ref<AnimationNodeAnimation> anim;
+ anim.instance();
+
+ anim->set_animation(animations_to_add[p_index]);
+
+ updating = true;
+ undo_redo->create_action("Add Animation Point");
+ undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
+ undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+ updating = false;
+
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_tool_switch(int p_tool) {
+
+ if (p_tool == 0) {
+ tool_erase->show();
+ tool_erase_sep->show();
+ } else {
+ tool_erase->hide();
+ tool_erase_sep->hide();
+ }
+
+ _update_tool_erase();
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() {
+ if (updating)
+ return;
+
+ if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
+ float pos = blend_space->get_blend_point_position(selected_point);
+
+ if (dragging_selected) {
+ pos += drag_ofs.x;
+
+ if (snap->is_pressed()) {
+ pos = Math::stepify(pos, blend_space->get_snap());
+ }
+ }
+
+ updating = true;
+ edit_value->set_value(pos);
+ updating = false;
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_update_tool_erase() {
+
+ bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count();
+ tool_erase->set_disabled(!point_valid);
+
+ if (point_valid) {
+ Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
+
+ if (EditorNode::get_singleton()->item_has_editor(an.ptr())) {
+ open_editor->show();
+ } else {
+ open_editor->hide();
+ }
+
+ edit_hb->show();
+ } else {
+ edit_hb->hide();
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_erase_selected() {
+ if (selected_point != -1) {
+ updating = true;
+
+ undo_redo->create_action("Remove BlendSpace1D Point");
+ undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
+ undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->commit_action();
+
+ updating = false;
+
+ blend_space_draw->update();
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) {
+ if (updating)
+ return;
+
+ updating = true;
+ undo_redo->create_action("Move BlendSpace1D Node Point");
+ undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
+ undo_redo->add_do_method(this, "_update_space");
+ undo_redo->add_undo_method(this, "_update_space");
+ undo_redo->add_do_method(this, "_update_edited_point_pos");
+ undo_redo->add_undo_method(this, "_update_edited_point_pos");
+ undo_redo->commit_action();
+ updating = false;
+
+ blend_space_draw->update();
+}
+
+void AnimationNodeBlendSpace1DEditor::_open_editor() {
+
+ if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
+ Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
+ ERR_FAIL_COND(an.is_null());
+ EditorNode::get_singleton()->edit_item(an.ptr());
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_goto_parent() {
+ EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr());
+}
+
+void AnimationNodeBlendSpace1DEditor::_removed_from_graph() {
+ EditorNode::get_singleton()->edit_item(NULL);
+}
+
+void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ tool_blend->set_icon(get_icon("EditPivot", "EditorIcons"));
+ tool_select->set_icon(get_icon("ToolSelect", "EditorIcons"));
+ tool_create->set_icon(get_icon("EditKey", "EditorIcons"));
+ tool_erase->set_icon(get_icon("Remove", "EditorIcons"));
+ snap->set_icon(get_icon("SnapGrid", "EditorIcons"));
+ open_editor->set_icon(get_icon("Edit", "EditorIcons"));
+ goto_parent->set_icon(get_icon("MoveUp", "EditorIcons"));
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+ String error;
+
+ if (!blend_space->get_tree()) {
+ error = TTR("BlendSpace1D does not belong to an AnimationTree node.");
+ } else if (!blend_space->get_tree()->is_active()) {
+ error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
+ } else if (blend_space->get_tree()->is_state_invalid()) {
+ error = blend_space->get_tree()->get_invalid_state_reason();
+ }
+
+ if (error != error_label->get_text()) {
+ error_label->set_text(error);
+ if (error != String()) {
+ error_panel->show();
+ } else {
+ error_panel->hide();
+ }
+ }
+ }
+}
+
+void AnimationNodeBlendSpace1DEditor::_bind_methods() {
+ ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpace1DEditor::_blend_space_gui_input);
+ ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpace1DEditor::_blend_space_draw);
+ ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpace1DEditor::_config_changed);
+ ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpace1DEditor::_labels_changed);
+ ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace1DEditor::_update_space);
+ ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpace1DEditor::_snap_toggled);
+ ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpace1DEditor::_tool_switch);
+ ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpace1DEditor::_erase_selected);
+ ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace1DEditor::_update_tool_erase);
+ ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpace1DEditor::_edit_point_pos);
+
+ ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpace1DEditor::_add_menu_type);
+ ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace1DEditor::_add_animation_type);
+
+ ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos);
+
+ ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace1DEditor::_open_editor);
+ ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace1DEditor::_goto_parent);
+
+ ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace1DEditor::_removed_from_graph);
+}
+
+void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) {
+
+ if (blend_space.is_valid()) {
+ blend_space->disconnect("removed_from_graph", this, "_removed_from_graph");
+ }
+
+ if (p_blend_space) {
+ blend_space = Ref<AnimationNodeBlendSpace1D>(p_blend_space);
+ } else {
+ blend_space.unref();
+ }
+
+ if (blend_space.is_null()) {
+ hide();
+ } else {
+ blend_space->connect("removed_from_graph", this, "_removed_from_graph");
+
+ _update_space();
+ }
+}
+
+AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = NULL;
+
+AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
+ singleton = this;
+ updating = false;
+
+ HBoxContainer *top_hb = memnew(HBoxContainer);
+ add_child(top_hb);
+
+ Ref<ButtonGroup> bg;
+ bg.instance();
+
+ goto_parent_hb = memnew(HBoxContainer);
+ top_hb->add_child(goto_parent_hb);
+
+ goto_parent = memnew(ToolButton);
+ goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED);
+ goto_parent_hb->add_child(goto_parent);
+ goto_parent_hb->add_child(memnew(VSeparator));
+ goto_parent_hb->hide();
+
+ tool_blend = memnew(ToolButton);
+ tool_blend->set_toggle_mode(true);
+ tool_blend->set_button_group(bg);
+ top_hb->add_child(tool_blend);
+ tool_blend->set_pressed(true);
+ tool_blend->set_tooltip(TTR("Set the blending position within the space"));
+ tool_blend->connect("pressed", this, "_tool_switch", varray(3));
+
+ tool_select = memnew(ToolButton);
+ tool_select->set_toggle_mode(true);
+ tool_select->set_button_group(bg);
+ top_hb->add_child(tool_select);
+ tool_select->set_tooltip(TTR("Select and move points, create points with RMB."));
+ tool_select->connect("pressed", this, "_tool_switch", varray(0));
+
+ tool_create = memnew(ToolButton);
+ tool_create->set_toggle_mode(true);
+ tool_create->set_button_group(bg);
+ top_hb->add_child(tool_create);
+ tool_create->set_tooltip(TTR("Create points."));
+ tool_create->connect("pressed", this, "_tool_switch", varray(1));
+
+ tool_erase_sep = memnew(VSeparator);
+ top_hb->add_child(tool_erase_sep);
+ tool_erase = memnew(ToolButton);
+ top_hb->add_child(tool_erase);
+ tool_erase->set_tooltip(TTR("Erase points."));
+ tool_erase->connect("pressed", this, "_erase_selected");
+
+ top_hb->add_child(memnew(VSeparator));
+
+ snap = memnew(ToolButton);
+ snap->set_toggle_mode(true);
+ top_hb->add_child(snap);
+ snap->set_pressed(true);
+ snap->connect("pressed", this, "_snap_toggled");
+
+ snap_value = memnew(SpinBox);
+ top_hb->add_child(snap_value);
+ snap_value->set_min(0.01);
+ snap_value->set_step(0.01);
+ snap_value->set_max(1000);
+
+ edit_hb = memnew(HBoxContainer);
+ top_hb->add_child(edit_hb);
+ edit_hb->add_child(memnew(VSeparator));
+ edit_hb->add_child(memnew(Label(TTR("Point"))));
+
+ edit_value = memnew(SpinBox);
+ edit_hb->add_child(edit_value);
+ edit_value->set_min(-1000);
+ edit_value->set_max(1000);
+ edit_value->set_step(0.01);
+ edit_value->connect("value_changed", this, "_edit_point_pos");
+
+ open_editor = memnew(Button);
+ edit_hb->add_child(open_editor);
+ open_editor->set_text(TTR("Open Editor"));
+ open_editor->connect("pressed", this, "_open_editor", varray(), CONNECT_DEFERRED);
+
+ edit_hb->hide();
+ open_editor->hide();
+
+ VBoxContainer *main_vb = memnew(VBoxContainer);
+ add_child(main_vb);
+ main_vb->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ panel = memnew(PanelContainer);
+ panel->set_clip_contents(true);
+ main_vb->add_child(panel);
+ panel->set_h_size_flags(SIZE_EXPAND_FILL);
+ panel->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ blend_space_draw = memnew(Control);
+ blend_space_draw->connect("gui_input", this, "_blend_space_gui_input");
+ blend_space_draw->connect("draw", this, "_blend_space_draw");
+ blend_space_draw->set_focus_mode(FOCUS_ALL);
+
+ panel->add_child(blend_space_draw);
+
+ {
+ HBoxContainer *bottom_hb = memnew(HBoxContainer);
+ main_vb->add_child(bottom_hb);
+ bottom_hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ min_value = memnew(SpinBox);
+ min_value->set_max(0);
+ min_value->set_min(-10000);
+ min_value->set_step(0.01);
+
+ max_value = memnew(SpinBox);
+ max_value->set_max(10000);
+ max_value->set_min(0.01);
+ max_value->set_step(0.01);
+
+ label_value = memnew(LineEdit);
+ label_value->set_expand_to_text_length(true);
+
+ // now add
+
+ bottom_hb->add_child(min_value);
+ bottom_hb->add_spacer();
+ bottom_hb->add_child(label_value);
+ bottom_hb->add_spacer();
+ bottom_hb->add_child(max_value);
+ }
+
+ snap_value->connect("value_changed", this, "_config_changed");
+ min_value->connect("value_changed", this, "_config_changed");
+ max_value->connect("value_changed", this, "_config_changed");
+ label_value->connect("text_changed", this, "_labels_changed");
+
+ error_panel = memnew(PanelContainer);
+ add_child(error_panel);
+
+ error_label = memnew(Label);
+ error_panel->add_child(error_label);
+ error_label->set_text("hmmm");
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("index_pressed", this, "_add_menu_type");
+
+ animations_menu = memnew(PopupMenu);
+ menu->add_child(animations_menu);
+ animations_menu->set_name("animations");
+ animations_menu->connect("index_pressed", this, "_add_animation_type");
+
+ selected_point = -1;
+ dragging_selected = false;
+ dragging_selected_attempt = false;
+
+ set_custom_minimum_size(Size2(0, 150 * EDSCALE));
+}
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
new file mode 100644
index 0000000000..52139626e6
--- /dev/null
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -0,0 +1,117 @@
+#ifndef ANIMATION_BLEND_SPACE_1D_EDITOR_H
+#define ANIMATION_BLEND_SPACE_1D_EDITOR_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/property_editor.h"
+#include "scene/animation/animation_blend_space_1d.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+
+class AnimationNodeBlendSpace1DEditor : public VBoxContainer {
+
+ GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer)
+
+ Ref<AnimationNodeBlendSpace1D> blend_space;
+
+ HBoxContainer *goto_parent_hb;
+ ToolButton *goto_parent;
+
+ PanelContainer *panel;
+ ToolButton *tool_blend;
+ ToolButton *tool_select;
+ ToolButton *tool_create;
+ VSeparator *tool_erase_sep;
+ ToolButton *tool_erase;
+ ToolButton *snap;
+ SpinBox *snap_value;
+
+ LineEdit *label_value;
+ SpinBox *max_value;
+ SpinBox *min_value;
+
+ HBoxContainer *edit_hb;
+ SpinBox *edit_value;
+ Button *open_editor;
+
+ int selected_point;
+
+ Control *blend_space_draw;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ bool updating;
+
+ UndoRedo *undo_redo;
+
+ static AnimationNodeBlendSpace1DEditor *singleton;
+
+ void _blend_space_gui_input(const Ref<InputEvent> &p_event);
+ void _blend_space_draw();
+
+ void _update_space();
+
+ void _config_changed(double);
+ void _labels_changed(String);
+ void _snap_toggled();
+
+ PopupMenu *menu;
+ PopupMenu *animations_menu;
+ Vector<String> animations_to_add;
+ float add_point_pos;
+ Vector<float> points;
+
+ bool dragging_selected_attempt;
+ bool dragging_selected;
+ Vector2 drag_from;
+ Vector2 drag_ofs;
+
+ void _add_menu_type(int p_index);
+ void _add_animation_type(int p_index);
+
+ void _tool_switch(int p_tool);
+ void _update_edited_point_pos();
+ void _update_tool_erase();
+ void _erase_selected();
+ void _edit_point_pos(double);
+ void _open_editor();
+
+ void _goto_parent();
+
+ void _removed_from_graph();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; }
+ void edit(AnimationNodeBlendSpace1D *p_blend_space);
+ AnimationNodeBlendSpace1DEditor();
+};
+
+class AnimationNodeBlendSpace1DEditorPlugin : public EditorPlugin {
+
+ GDCLASS(AnimationNodeBlendSpace1DEditorPlugin, EditorPlugin)
+
+ AnimationNodeBlendSpace1DEditor *anim_tree_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ virtual String get_name() const { return "BlendSpace1D"; }
+
+ bool has_main_screen() const { return false; }
+
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node);
+ ~AnimationNodeBlendSpace1DEditorPlugin();
+};
+
+#endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H
diff --git a/editor/plugins/animation_blend_space_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 9118211bf2..8d17062248 100644
--- a/editor/plugins/animation_blend_space_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -1,4 +1,4 @@
-#include "animation_blend_space_editor.h"
+#include "animation_blend_space_2d_editor.h"
#include "core/io/resource_loader.h"
#include "core/project_settings.h"
@@ -11,14 +11,14 @@
#include "scene/gui/panel.h"
#include "scene/main/viewport.h"
-void AnimationNodeBlendSpaceEditor::edit(AnimationNodeBlendSpace *p_blend_space) {
+void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) {
if (blend_space.is_valid()) {
blend_space->disconnect("removed_from_graph", this, "_removed_from_graph");
}
if (p_blend_space) {
- blend_space = Ref<AnimationNodeBlendSpace>(p_blend_space);
+ blend_space = Ref<AnimationNodeBlendSpace2D>(p_blend_space);
} else {
blend_space.unref();
}
@@ -32,7 +32,7 @@ void AnimationNodeBlendSpaceEditor::edit(AnimationNodeBlendSpace *p_blend_space)
}
}
-void AnimationNodeBlendSpaceEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
+void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) {
@@ -54,7 +54,7 @@ void AnimationNodeBlendSpaceEditor::_blend_space_gui_input(const Ref<InputEvent>
ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
menu->add_submenu_item(TTR("Add Animation"), "animations");
- AnimationGraphPlayer *gp = blend_space->get_graph_player();
+ AnimationTree *gp = blend_space->get_tree();
ERR_FAIL_COND(!gp);
if (gp && gp->has_node(gp->get_animation_player())) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player()));
@@ -203,7 +203,7 @@ void AnimationNodeBlendSpaceEditor::_blend_space_gui_input(const Ref<InputEvent>
blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
blend_pos += blend_space->get_min_space();
- blend_space->set_blend_pos(blend_pos);
+ blend_space->set_blend_position(blend_pos);
blend_space_draw->update();
}
@@ -237,12 +237,12 @@ void AnimationNodeBlendSpaceEditor::_blend_space_gui_input(const Ref<InputEvent>
blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
blend_pos += blend_space->get_min_space();
- blend_space->set_blend_pos(blend_pos);
+ blend_space->set_blend_position(blend_pos);
blend_space_draw->update();
}
}
-void AnimationNodeBlendSpaceEditor::_add_menu_type(int p_index) {
+void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
String type = menu->get_item_metadata(p_index);
@@ -265,7 +265,7 @@ void AnimationNodeBlendSpaceEditor::_add_menu_type(int p_index) {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_add_animation_type(int p_index) {
+void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) {
Ref<AnimationNodeAnimation> anim;
anim.instance();
@@ -284,7 +284,7 @@ void AnimationNodeBlendSpaceEditor::_add_animation_type(int p_index) {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_update_tool_erase() {
+void AnimationNodeBlendSpace2DEditor::_update_tool_erase() {
tool_erase->set_disabled(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count()));
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
@@ -299,7 +299,7 @@ void AnimationNodeBlendSpaceEditor::_update_tool_erase() {
}
}
-void AnimationNodeBlendSpaceEditor::_tool_switch(int p_tool) {
+void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) {
making_triangle.clear();
if (p_tool == 2) {
@@ -325,7 +325,7 @@ void AnimationNodeBlendSpaceEditor::_tool_switch(int p_tool) {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_blend_space_draw() {
+void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
Color linecolor = get_color("font_color", "Label");
Color linecolor_soft = linecolor;
@@ -352,7 +352,7 @@ void AnimationNodeBlendSpaceEditor::_blend_space_draw() {
}
if (blend_space->get_min_space().x < 0) {
- int x = (blend_space->get_max_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width;
+ int x = (-blend_space->get_min_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width;
blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor);
blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height() + font->get_ascent()), "0", linecolor);
blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft);
@@ -490,13 +490,13 @@ void AnimationNodeBlendSpaceEditor::_blend_space_draw() {
color.a *= 0.5;
}
- Vector2 point = blend_space->get_blend_pos();
+ Vector2 point = blend_space->get_blend_position();
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
point *= s;
point.y = s.height - point.y;
if (blend_space->get_triangle_count()) {
- Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_pos());
+ Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position());
closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
closest *= s;
closest.y = s.height - closest.y;
@@ -515,12 +515,12 @@ void AnimationNodeBlendSpaceEditor::_blend_space_draw() {
}
}
-void AnimationNodeBlendSpaceEditor::_snap_toggled() {
+void AnimationNodeBlendSpace2DEditor::_snap_toggled() {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_update_space() {
+void AnimationNodeBlendSpace2DEditor::_update_space() {
if (updating)
return;
@@ -558,12 +558,12 @@ void AnimationNodeBlendSpaceEditor::_update_space() {
updating = false;
}
-void AnimationNodeBlendSpaceEditor::_config_changed(double) {
+void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
if (updating)
return;
updating = true;
- undo_redo->create_action("Change BlendSpace Limits");
+ undo_redo->create_action("Change BlendSpace2D Limits");
undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value()));
undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value()));
@@ -578,12 +578,12 @@ void AnimationNodeBlendSpaceEditor::_config_changed(double) {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_labels_changed(String) {
+void AnimationNodeBlendSpace2DEditor::_labels_changed(String) {
if (updating)
return;
updating = true;
- undo_redo->create_action("Change BlendSpace Labels", UndoRedo::MERGE_ENDS);
+ undo_redo->create_action("Change BlendSpace2D Labels", UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text());
undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label());
undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text());
@@ -594,12 +594,12 @@ void AnimationNodeBlendSpaceEditor::_labels_changed(String) {
updating = false;
}
-void AnimationNodeBlendSpaceEditor::_erase_selected() {
+void AnimationNodeBlendSpace2DEditor::_erase_selected() {
if (selected_point != -1) {
updating = true;
- undo_redo->create_action("Remove BlendSpace Point");
+ undo_redo->create_action("Remove BlendSpace2D Point");
undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
@@ -622,7 +622,7 @@ void AnimationNodeBlendSpaceEditor::_erase_selected() {
} else if (selected_triangle != -1) {
updating = true;
- undo_redo->create_action("Remove BlendSpace Triangle");
+ undo_redo->create_action("Remove BlendSpace2D Triangle");
undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle);
undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(selected_triangle, 0), blend_space->get_triangle_point(selected_triangle, 1), blend_space->get_triangle_point(selected_triangle, 2), selected_triangle);
@@ -635,7 +635,7 @@ void AnimationNodeBlendSpaceEditor::_erase_selected() {
}
}
-void AnimationNodeBlendSpaceEditor::_update_edited_point_pos() {
+void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
if (updating)
return;
@@ -655,7 +655,7 @@ void AnimationNodeBlendSpaceEditor::_update_edited_point_pos() {
}
}
-void AnimationNodeBlendSpaceEditor::_edit_point_pos(double) {
+void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
if (updating)
return;
updating = true;
@@ -672,7 +672,7 @@ void AnimationNodeBlendSpaceEditor::_edit_point_pos(double) {
blend_space_draw->update();
}
-void AnimationNodeBlendSpaceEditor::_notification(int p_what) {
+void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
@@ -693,12 +693,12 @@ void AnimationNodeBlendSpaceEditor::_notification(int p_what) {
String error;
- if (!blend_space->get_graph_player()) {
- error = TTR("BlendSpace does not belong to an AnimationGraphPlayer node.");
- } else if (!blend_space->get_graph_player()->is_active()) {
- error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails.");
- } else if (blend_space->get_graph_player()->is_state_invalid()) {
- error = blend_space->get_graph_player()->get_invalid_state_reason();
+ if (!blend_space->get_tree()) {
+ error = TTR("BlendSpace2D does not belong to an AnimationTree node.");
+ } else if (!blend_space->get_tree()->is_active()) {
+ error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
+ } else if (blend_space->get_tree()->is_state_invalid()) {
+ error = blend_space->get_tree()->get_invalid_state_reason();
} else if (blend_space->get_triangle_count() == 0) {
error = TTR("No triangles exist, so no blending can take place.");
}
@@ -714,7 +714,7 @@ void AnimationNodeBlendSpaceEditor::_notification(int p_what) {
}
}
-void AnimationNodeBlendSpaceEditor::_open_editor() {
+void AnimationNodeBlendSpace2DEditor::_open_editor() {
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
@@ -723,16 +723,16 @@ void AnimationNodeBlendSpaceEditor::_open_editor() {
}
}
-void AnimationNodeBlendSpaceEditor::_goto_parent() {
+void AnimationNodeBlendSpace2DEditor::_goto_parent() {
EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr());
}
-void AnimationNodeBlendSpaceEditor::_removed_from_graph() {
+void AnimationNodeBlendSpace2DEditor::_removed_from_graph() {
EditorNode::get_singleton()->edit_item(NULL);
}
-void AnimationNodeBlendSpaceEditor::_auto_triangles_toggled() {
+void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() {
undo_redo->create_action("Toggle Auto Triangles");
undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed());
@@ -742,35 +742,35 @@ void AnimationNodeBlendSpaceEditor::_auto_triangles_toggled() {
undo_redo->commit_action();
}
-void AnimationNodeBlendSpaceEditor::_bind_methods() {
+void AnimationNodeBlendSpace2DEditor::_bind_methods() {
- ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpaceEditor::_blend_space_gui_input);
- ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpaceEditor::_blend_space_draw);
- ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpaceEditor::_config_changed);
- ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpaceEditor::_labels_changed);
- ClassDB::bind_method("_update_space", &AnimationNodeBlendSpaceEditor::_update_space);
- ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpaceEditor::_snap_toggled);
- ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpaceEditor::_tool_switch);
- ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpaceEditor::_erase_selected);
- ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpaceEditor::_update_tool_erase);
- ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpaceEditor::_edit_point_pos);
+ ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpace2DEditor::_blend_space_gui_input);
+ ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpace2DEditor::_blend_space_draw);
+ ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpace2DEditor::_config_changed);
+ ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpace2DEditor::_labels_changed);
+ ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace2DEditor::_update_space);
+ ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpace2DEditor::_snap_toggled);
+ ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpace2DEditor::_tool_switch);
+ ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpace2DEditor::_erase_selected);
+ ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase);
+ ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpace2DEditor::_edit_point_pos);
- ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpaceEditor::_add_menu_type);
- ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpaceEditor::_add_animation_type);
+ ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpace2DEditor::_add_menu_type);
+ ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace2DEditor::_add_animation_type);
- ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpaceEditor::_update_edited_point_pos);
+ ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos);
- ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpaceEditor::_open_editor);
- ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpaceEditor::_goto_parent);
+ ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor);
+ ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent);
- ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpaceEditor::_removed_from_graph);
+ ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph);
- ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpaceEditor::_auto_triangles_toggled);
+ ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled);
}
-AnimationNodeBlendSpaceEditor *AnimationNodeBlendSpaceEditor::singleton = NULL;
+AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL;
-AnimationNodeBlendSpaceEditor::AnimationNodeBlendSpaceEditor() {
+AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
singleton = this;
updating = false;
@@ -982,17 +982,17 @@ AnimationNodeBlendSpaceEditor::AnimationNodeBlendSpaceEditor() {
dragging_selected_attempt = false;
}
-void AnimationNodeBlendSpaceEditorPlugin::edit(Object *p_object) {
+void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) {
- anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace>(p_object));
+ anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(p_object));
}
-bool AnimationNodeBlendSpaceEditorPlugin::handles(Object *p_object) const {
+bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("AnimationNodeBlendSpace");
+ return p_object->is_class("AnimationNodeBlendSpace2D");
}
-void AnimationNodeBlendSpaceEditorPlugin::make_visible(bool p_visible) {
+void AnimationNodeBlendSpace2DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
//editor->hide_animation_player_editors();
@@ -1009,15 +1009,15 @@ void AnimationNodeBlendSpaceEditorPlugin::make_visible(bool p_visible) {
}
}
-AnimationNodeBlendSpaceEditorPlugin::AnimationNodeBlendSpaceEditorPlugin(EditorNode *p_node) {
+AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- anim_tree_editor = memnew(AnimationNodeBlendSpaceEditor);
+ anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor);
anim_tree_editor->set_custom_minimum_size(Size2(0, 300));
- button = editor->add_bottom_panel_item(TTR("BlendSpace"), anim_tree_editor);
+ button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor);
button->hide();
}
-AnimationNodeBlendSpaceEditorPlugin::~AnimationNodeBlendSpaceEditorPlugin() {
+AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() {
}
diff --git a/editor/plugins/animation_blend_space_editor.h b/editor/plugins/animation_blend_space_2d_editor.h
index 66d2ec6cae..a0e497804e 100644
--- a/editor/plugins/animation_blend_space_editor.h
+++ b/editor/plugins/animation_blend_space_2d_editor.h
@@ -1,10 +1,10 @@
-#ifndef ANIMATION_BLEND_SPACE_EDITOR_H
-#define ANIMATION_BLEND_SPACE_EDITOR_H
+#ifndef ANIMATION_BLEND_SPACE_2D_EDITOR_H
+#define ANIMATION_BLEND_SPACE_2D_EDITOR_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "editor/property_editor.h"
-#include "scene/animation/animation_blend_space.h"
+#include "scene/animation/animation_blend_space_2d.h"
#include "scene/gui/button.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
@@ -13,11 +13,11 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
-class AnimationNodeBlendSpaceEditor : public VBoxContainer {
+class AnimationNodeBlendSpace2DEditor : public VBoxContainer {
- GDCLASS(AnimationNodeBlendSpaceEditor, VBoxContainer);
+ GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer);
- Ref<AnimationNodeBlendSpace> blend_space;
+ Ref<AnimationNodeBlendSpace2D> blend_space;
HBoxContainer *goto_parent_hb;
ToolButton *goto_parent;
@@ -59,7 +59,7 @@ class AnimationNodeBlendSpaceEditor : public VBoxContainer {
UndoRedo *undo_redo;
- static AnimationNodeBlendSpaceEditor *singleton;
+ static AnimationNodeBlendSpace2DEditor *singleton;
void _blend_space_gui_input(const Ref<InputEvent> &p_event);
void _blend_space_draw();
@@ -104,27 +104,27 @@ protected:
static void _bind_methods();
public:
- static AnimationNodeBlendSpaceEditor *get_singleton() { return singleton; }
- void edit(AnimationNodeBlendSpace *p_blend_space);
- AnimationNodeBlendSpaceEditor();
+ static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; }
+ void edit(AnimationNodeBlendSpace2D *p_blend_space);
+ AnimationNodeBlendSpace2DEditor();
};
-class AnimationNodeBlendSpaceEditorPlugin : public EditorPlugin {
+class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin {
- GDCLASS(AnimationNodeBlendSpaceEditorPlugin, EditorPlugin);
+ GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin);
- AnimationNodeBlendSpaceEditor *anim_tree_editor;
+ AnimationNodeBlendSpace2DEditor *anim_tree_editor;
EditorNode *editor;
Button *button;
public:
- virtual String get_name() const { return "BlendSpace"; }
+ virtual String get_name() const { return "BlendSpace2D"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
- AnimationNodeBlendSpaceEditorPlugin(EditorNode *p_node);
- ~AnimationNodeBlendSpaceEditorPlugin();
+ AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node);
+ ~AnimationNodeBlendSpace2DEditorPlugin();
};
-#endif // ANIMATION_BLEND_SPACE_EDITOR_H
+#endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 0875c93a16..c00ad451fa 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -169,7 +169,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() {
ProgressBar *pb = memnew(ProgressBar);
- AnimationGraphPlayer *player = anim->get_graph_player();
+ AnimationTree *player = anim->get_tree();
if (player->has_node(player->get_animation_player())) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player()));
if (ap) {
@@ -246,7 +246,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5;
- anode->set_position(instance_pos);
+ anode->set_position(instance_pos / EDSCALE);
String base_name = add_options[p_idx].name;
int base = 1;
@@ -417,14 +417,14 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano
if (updating || _filter_edit != anode)
return false;
- NodePath player_path = anode->get_graph_player()->get_animation_player();
+ NodePath player_path = anode->get_tree()->get_animation_player();
- if (!anode->get_graph_player()->has_node(player_path)) {
+ if (!anode->get_tree()->has_node(player_path)) {
EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names."));
return false;
}
- AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_graph_player()->get_node(player_path));
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path));
if (!player) {
EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names."));
return false;
@@ -603,12 +603,12 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
String error;
- if (!blend_tree->get_graph_player()) {
- error = TTR("BlendTree does not belong to an AnimationGraphPlayer node.");
- } else if (!blend_tree->get_graph_player()->is_active()) {
- error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails.");
- } else if (blend_tree->get_graph_player()->is_state_invalid()) {
- error = blend_tree->get_graph_player()->get_invalid_state_reason();
+ if (!blend_tree->get_tree()) {
+ error = TTR("BlendTree does not belong to an AnimationTree node.");
+ } else if (!blend_tree->get_tree()->is_active()) {
+ error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
+ } else if (blend_tree->get_tree()->is_state_invalid()) {
+ error = blend_tree->get_tree()->get_invalid_state_reason();
}
if (error != error_label->get_text()) {
@@ -624,13 +624,13 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
blend_tree->get_node_connections(&conns);
for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) {
float activity = 0;
- if (blend_tree->get_graph_player() && !blend_tree->get_graph_player()->is_state_invalid()) {
+ if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) {
activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index);
}
graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity);
}
- AnimationGraphPlayer *graph_player = blend_tree->get_graph_player();
+ AnimationTree *graph_player = blend_tree->get_tree();
AnimationPlayer *player = NULL;
if (graph_player->has_node(graph_player->get_animation_player())) {
player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player()));
@@ -767,14 +767,17 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot"));
- add_options.push_back(AddOption("Add", "AnimationNodeAdd"));
+ add_options.push_back(AddOption("Add2", "AnimationNodeAdd2"));
+ add_options.push_back(AddOption("Add3", "AnimationNodeAdd3"));
add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2"));
add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3"));
add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek"));
add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale"));
add_options.push_back(AddOption("Transition", "AnimationNodeTransition"));
add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree"));
- add_options.push_back(AddOption("BlendSpace", "AnimationNodeBlendSpace"));
+ add_options.push_back(AddOption("BlendSpace1D", "AnimationNodeBlendSpace1D"));
+ add_options.push_back(AddOption("BlendSpace2D", "AnimationNodeBlendSpace2D"));
+ add_options.push_back(AddOption("StateMachine", "AnimationNodeStateMachine"));
_update_options_menu();
error_panel = memnew(PanelContainer);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 3477a6ec30..23eeef9f20 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -144,6 +144,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
ITEM_ICON(TOOL_DUPLICATE_ANIM, "Duplicate");
ITEM_ICON(TOOL_RENAME_ANIM, "Rename");
ITEM_ICON(TOOL_EDIT_TRANSITIONS, "Blend");
+ ITEM_ICON(TOOL_EDIT_RESOURCE, "Edit");
ITEM_ICON(TOOL_REMOVE_ANIM, "Remove");
//ITEM_ICON(TOOL_COPY_ANIM, "Copy");
//ITEM_ICON(TOOL_PASTE_ANIM, "Paste");
@@ -327,6 +328,9 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
}
autoplay->set_pressed(current == player->get_autoplay());
+
+ AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ EditorNode::get_singleton()->update_keying();
}
void AnimationPlayerEditor::_animation_new() {
@@ -849,8 +853,11 @@ void AnimationPlayerEditor::_update_player() {
active_idx = animation->get_item_count() - 1;
}
- if (!player)
+ if (!player) {
+ AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ EditorNode::get_singleton()->update_keying();
return;
+ }
updating = false;
if (active_idx != -1) {
@@ -863,6 +870,8 @@ void AnimationPlayerEditor::_update_player() {
animation->select(0);
autoplay->set_pressed(animation->get_item_text(0) == player->get_autoplay());
_animation_selected(0);
+ } else {
+ _animation_selected(0);
}
//pause->set_pressed(player->is_paused());
@@ -1102,9 +1111,12 @@ void AnimationPlayerEditor::_animation_about_to_show_menu() {
void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
- String current = animation->get_item_text(animation->get_selected());
+ String current;
+ if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count())
+ current = animation->get_item_text(animation->get_selected());
+
Ref<Animation> anim;
- if (current != "") {
+ if (current != String()) {
anim = player->get_animation(current);
}
@@ -1667,6 +1679,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
tool_anim->get_popup()->add_separator();
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/rename_animation", TTR("Rename...")), TOOL_RENAME_ANIM);
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/edit_transitions", TTR("Edit Transitions...")), TOOL_EDIT_TRANSITIONS);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation_in_inspector", TTR("Open in Inspector")), TOOL_EDIT_RESOURCE);
tool_anim->get_popup()->add_separator();
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/remove_animation", TTR("Remove")), TOOL_REMOVE_ANIM);
hb->add_child(tool_anim);
@@ -1684,6 +1697,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
//tool_anim->get_popup()->add_separator();
//tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
+ hb->add_child(memnew(VSeparator));
+
track_editor = memnew(AnimationTrackEditor);
hb->add_child(track_editor->get_edit_menu());
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
new file mode 100644
index 0000000000..04bd5f0cec
--- /dev/null
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -0,0 +1,1313 @@
+#include "animation_state_machine_editor.h"
+
+#include "core/io/resource_loader.h"
+#include "core/project_settings.h"
+#include "math/delaunay.h"
+#include "os/input.h"
+#include "os/keyboard.h"
+#include "scene/animation/animation_blend_tree.h"
+#include "scene/animation/animation_player.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/panel.h"
+#include "scene/main/viewport.h"
+
+void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_machine) {
+
+ if (state_machine.is_valid()) {
+ state_machine->disconnect("removed_from_graph", this, "_removed_from_graph");
+ }
+
+ if (p_state_machine) {
+ state_machine = Ref<AnimationNodeStateMachine>(p_state_machine);
+ } else {
+ state_machine.unref();
+ }
+
+ if (state_machine.is_null()) {
+ hide();
+ } else {
+ state_machine->connect("removed_from_graph", this, "_removed_from_graph");
+
+ selected_transition_from = StringName();
+ selected_transition_to = StringName();
+ selected_node = StringName();
+ _update_mode();
+ _update_graph();
+ }
+}
+
+void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventKey> k = p_event;
+ if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) {
+ if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) {
+ _erase_selected();
+ accept_event();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ //Add new node
+ if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == BUTTON_LEFT))) {
+ menu->clear();
+ animations_menu->clear();
+ animations_to_add.clear();
+ List<StringName> classes;
+ classes.sort_custom<StringName::AlphCompare>();
+
+ ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
+ menu->add_submenu_item(TTR("Add Animation"), "animations");
+
+ AnimationTree *gp = state_machine->get_tree();
+ ERR_FAIL_COND(!gp);
+ if (gp && gp->has_node(gp->get_animation_player())) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player()));
+ if (ap) {
+ List<StringName> names;
+ ap->get_animation_list(&names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ animations_menu->add_icon_item(get_icon("Animation", "EditorIcons"), E->get());
+ animations_to_add.push_back(E->get());
+ }
+ }
+ }
+
+ for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
+
+ String name = String(E->get()).replace_first("AnimationNode", "");
+ if (name == "Animation")
+ continue; // nope
+ int idx = menu->get_item_count();
+ menu->add_item(vformat("Add %s", name));
+ menu->set_item_metadata(idx, E->get());
+ }
+
+ menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position()));
+ menu->popup();
+ add_node_pos = mb->get_position() / EDSCALE + state_machine->get_graph_offset();
+ }
+
+ // select node or push a field inside
+ if (mb.is_valid() && !mb->get_shift() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ selected_transition_from = StringName();
+ selected_transition_to = StringName();
+ selected_node = StringName();
+
+ for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
+
+ if (node_rects[i].play.has_point(mb->get_position())) { //edit name
+ if (play_mode->get_selected() == 1 || !state_machine->is_playing()) {
+ //start
+ state_machine->start(node_rects[i].node_name);
+ } else {
+ //travel
+ if (!state_machine->travel(node_rects[i].node_name)) {
+
+ state_machine->start(node_rects[i].node_name);
+ //removing this due to usability..
+ //error_time = 5;
+ //error_text = vformat(TTR("No path found from '%s' to '%s'."), state_machine->get_current_node(), node_rects[i].node_name);
+ }
+ }
+ state_machine_draw->update();
+ return;
+ }
+
+ if (node_rects[i].name.has_point(mb->get_position())) { //edit name
+
+ Ref<StyleBox> line_sb = get_stylebox("normal", "LineEdit");
+
+ Rect2 edit_rect = node_rects[i].name;
+ edit_rect.position -= line_sb->get_offset();
+ edit_rect.size += line_sb->get_minimum_size();
+
+ name_edit->set_global_position(state_machine_draw->get_global_transform().xform(edit_rect.position));
+ name_edit->set_size(edit_rect.size);
+ name_edit->set_text(node_rects[i].node_name);
+ name_edit->show_modal();
+ name_edit->grab_focus();
+ name_edit->select_all();
+
+ prev_name = node_rects[i].node_name;
+ return;
+ }
+
+ if (node_rects[i].edit.has_point(mb->get_position())) { //edit name
+ call_deferred("_open_editor", node_rects[i].node_name);
+ return;
+ }
+
+ if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
+ selected_node = node_rects[i].node_name;
+
+ Ref<AnimationNode> anode = state_machine->get_node(selected_node);
+ EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
+ state_machine_draw->update();
+ dragging_selected_attempt = true;
+ dragging_selected = false;
+ drag_from = mb->get_position();
+ snap_x = StringName();
+ snap_y = StringName();
+ _update_mode();
+ return;
+ }
+ }
+
+ //test the lines now
+ int closest = -1;
+ float closest_d = 1e20;
+ for (int i = 0; i < transition_lines.size(); i++) {
+
+ Vector2 s[2] = {
+ transition_lines[i].from,
+ transition_lines[i].to
+ };
+ Vector2 cpoint = Geometry::get_closest_point_to_segment_2d(mb->get_position(), s);
+ float d = cpoint.distance_to(mb->get_position());
+ if (d > transition_lines[i].width) {
+ continue;
+ }
+
+ if (d < closest_d) {
+ closest = i;
+ closest_d = d;
+ }
+ }
+
+ if (closest >= 0) {
+ selected_transition_from = transition_lines[closest].from_node;
+ selected_transition_to = transition_lines[closest].to_node;
+
+ Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest);
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
+ }
+
+ state_machine_draw->update();
+ _update_mode();
+ }
+
+ //end moving node
+ if (mb.is_valid() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
+
+ if (dragging_selected) {
+
+ Ref<AnimationNode> an = state_machine->get_node(selected_node);
+ updating = true;
+ undo_redo->create_action("Move Node");
+ undo_redo->add_do_method(an.ptr(), "set_position", an->get_position() + drag_ofs / EDSCALE);
+ undo_redo->add_undo_method(an.ptr(), "set_position", an->get_position());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+ }
+ snap_x = StringName();
+ snap_y = StringName();
+
+ dragging_selected_attempt = false;
+ dragging_selected = false;
+ state_machine_draw->update();
+ }
+
+ //connect nodes
+ if (mb.is_valid() && ((tool_select->is_pressed() && mb->get_shift()) || tool_connect->is_pressed()) && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+
+ for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
+ if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
+ connecting = true;
+ connecting_from = node_rects[i].node_name;
+ connecting_to = mb->get_position();
+ connecting_to_node = StringName();
+ return;
+ }
+ }
+ }
+
+ //end connecting nodes
+ if (mb.is_valid() && connecting && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
+
+ if (connecting_to_node != StringName()) {
+
+ if (state_machine->has_transition(connecting_from, connecting_to_node)) {
+ EditorNode::get_singleton()->show_warning("Transition exists!");
+
+ } else {
+
+ Ref<AnimationNodeStateMachineTransition> tr;
+ tr.instance();
+ tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()));
+
+ updating = true;
+ undo_redo->create_action("Add Transition");
+ undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr);
+ undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ selected_transition_from = connecting_from;
+ selected_transition_to = connecting_to_node;
+
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
+ _update_mode();
+ }
+ }
+ connecting_to_node = StringName();
+ connecting = false;
+ state_machine_draw->update();
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ //pan window
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
+
+ h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
+ v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
+ }
+
+ //move mouse while connecting
+ if (mm.is_valid() && connecting) {
+
+ connecting_to = mm->get_position();
+ connecting_to_node = StringName();
+ state_machine_draw->update();
+
+ for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
+ if (node_rects[i].node_name != connecting_from && node_rects[i].node.has_point(connecting_to)) { //select node since nothing else was selected
+ connecting_to_node = node_rects[i].node_name;
+ return;
+ }
+ }
+ }
+
+ //move mouse while moving a node
+ if (mm.is_valid() && dragging_selected_attempt) {
+
+ dragging_selected = true;
+ drag_ofs = mm->get_position() - drag_from;
+ snap_x = StringName();
+ snap_y = StringName();
+ {
+ //snap
+ Vector2 cpos = state_machine->get_node(selected_node)->get_position() + drag_ofs / EDSCALE;
+ List<StringName> nodes;
+ state_machine->get_node_list(&nodes);
+
+ float best_d_x = 1e20;
+ float best_d_y = 1e20;
+
+ for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
+ if (E->get() == selected_node)
+ continue;
+ Vector2 npos = state_machine->get_node(E->get())->get_position();
+
+ float d_x = ABS(npos.x - cpos.x);
+ if (d_x < MIN(5, best_d_x)) {
+ drag_ofs.x -= cpos.x - npos.x;
+ best_d_x = d_x;
+ snap_x = E->get();
+ }
+
+ float d_y = ABS(npos.y - cpos.y);
+ if (d_y < MIN(5, best_d_y)) {
+ drag_ofs.y -= cpos.y - npos.y;
+ best_d_y = d_y;
+ snap_y = E->get();
+ }
+ }
+ }
+
+ state_machine_draw->update();
+ }
+
+ //put ibeam (text cursor) over names to make it clearer that they are editable
+ if (mm.is_valid()) {
+
+ state_machine_draw->grab_focus();
+
+ bool over_text_now = false;
+ String new_over_node = StringName();
+ int new_over_node_what = -1;
+ if (tool_select->is_pressed()) {
+
+ for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
+
+ if (node_rects[i].name.has_point(mm->get_position())) {
+ over_text_now = true;
+ break;
+ }
+
+ if (node_rects[i].node.has_point(mm->get_position())) {
+ new_over_node = node_rects[i].node_name;
+ if (node_rects[i].play.has_point(mm->get_position())) {
+ new_over_node_what = 0;
+ }
+ if (node_rects[i].edit.has_point(mm->get_position())) {
+ new_over_node_what = 1;
+ }
+ }
+ }
+ }
+
+ if (new_over_node != over_node || new_over_node_what != over_node_what) {
+ over_node = new_over_node;
+ over_node_what = new_over_node_what;
+ state_machine_draw->update();
+ }
+
+ if (over_text != over_text_now) {
+
+ if (over_text_now) {
+ state_machine_draw->set_default_cursor_shape(CURSOR_IBEAM);
+ } else {
+ state_machine_draw->set_default_cursor_shape(CURSOR_ARROW);
+ }
+
+ over_text = over_text_now;
+ }
+ }
+}
+
+void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) {
+
+ String type = menu->get_item_metadata(p_index);
+
+ Object *obj = ClassDB::instance(type);
+ ERR_FAIL_COND(!obj);
+ AnimationNode *an = Object::cast_to<AnimationNode>(obj);
+ ERR_FAIL_COND(!an);
+
+ Ref<AnimationNode> node(an);
+ node->set_position(add_node_pos);
+
+ String base_name = type.replace_first("AnimationNode", "");
+ int base = 1;
+ String name = base_name;
+ while (state_machine->has_node(name)) {
+ base++;
+ name = base_name + " " + itos(base);
+ }
+
+ updating = true;
+ undo_redo->create_action("Add Node");
+ undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node);
+ undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ state_machine_draw->update();
+}
+
+void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) {
+
+ Ref<AnimationNodeAnimation> anim;
+ anim.instance();
+
+ anim->set_animation(animations_to_add[p_index]);
+
+ String base_name = animations_to_add[p_index];
+ int base = 1;
+ String name = base_name;
+ while (state_machine->has_node(name)) {
+ base++;
+ name = base_name + " " + itos(base);
+ }
+
+ anim->set_position(add_node_pos);
+
+ updating = true;
+ undo_redo->create_action("Add Node");
+ undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim);
+ undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ state_machine_draw->update();
+}
+
+void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance) {
+
+ Color linecolor = get_color("font_color", "Label");
+ Color icon_color(1, 1, 1);
+ Color accent = get_color("accent_color", "Editor");
+
+ if (!p_enabled) {
+ linecolor.a *= 0.2;
+ icon_color.a *= 0.2;
+ accent.a *= 0.6;
+ }
+
+ Ref<Texture> icons[6] = {
+ get_icon("TransitionImmediateBig", "EditorIcons"),
+ get_icon("TransitionSyncBig", "EditorIcons"),
+ get_icon("TransitionEndBig", "EditorIcons"),
+ get_icon("TransitionImmediateAutoBig", "EditorIcons"),
+ get_icon("TransitionSyncAutoBig", "EditorIcons"),
+ get_icon("TransitionEndAutoBig", "EditorIcons")
+ };
+
+ if (p_selected) {
+ state_machine_draw->draw_line(p_from, p_to, accent, 6, true);
+ }
+
+ if (p_travel) {
+ linecolor = accent;
+ linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v());
+ }
+ state_machine_draw->draw_line(p_from, p_to, linecolor, 2, true);
+
+ Ref<Texture> icon = icons[p_mode + (p_auto_advance ? 3 : 0)];
+
+ Transform2D xf;
+ xf.elements[0] = (p_to - p_from).normalized();
+ xf.elements[1] = xf.elements[0].tangent();
+ xf.elements[2] = (p_from + p_to) * 0.5 - xf.elements[1] * icon->get_height() * 0.5 - xf.elements[0] * icon->get_height() * 0.5;
+
+ state_machine_draw->draw_set_transform_matrix(xf);
+ state_machine_draw->draw_texture(icon, Vector2(), icon_color);
+ state_machine_draw->draw_set_transform_matrix(Transform2D());
+}
+
+void AnimationNodeStateMachineEditor::_clip_src_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) {
+
+ if (r_to == r_from)
+ return;
+
+ //this could be optimized...
+ Vector2 n = (r_to - r_from).normalized();
+ while (p_rect.has_point(r_from)) {
+ r_from += n;
+ }
+}
+
+void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) {
+
+ if (r_to == r_from)
+ return;
+
+ //this could be optimized...
+ Vector2 n = (r_to - r_from).normalized();
+ while (p_rect.has_point(r_to)) {
+ r_to -= n;
+ }
+}
+
+void AnimationNodeStateMachineEditor::_state_machine_draw() {
+
+ Ref<StyleBox> style = get_stylebox("frame", "GraphNode");
+ Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode");
+
+ Ref<Font> font = get_font("title_font", "GraphNode");
+ Color font_color = get_color("title_color", "GraphNode");
+ Ref<Texture> play = get_icon("Play", "EditorIcons");
+ Ref<Texture> auto_play = get_icon("AutoPlay", "EditorIcons");
+ Ref<Texture> edit = get_icon("Edit", "EditorIcons");
+ Color accent = get_color("accent_color", "Editor");
+ Color linecolor = get_color("font_color", "Label");
+ linecolor.a *= 0.3;
+ Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode");
+
+ bool playing = state_machine->is_playing();
+ StringName current = state_machine->get_current_node();
+ StringName blend_from = state_machine->get_blend_from_node();
+ Vector<StringName> travel_path = state_machine->get_travel_path();
+
+ if (state_machine_draw->has_focus()) {
+ state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false);
+ }
+ int sep = 3 * EDSCALE;
+
+ List<StringName> nodes;
+ state_machine->get_node_list(&nodes);
+
+ node_rects.clear();
+ Rect2 scroll_range(Point2(), state_machine_draw->get_size());
+
+ //snap lines
+ if (dragging_selected) {
+
+ Vector2 from = (state_machine->get_node(selected_node)->get_position() * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE;
+ if (snap_x != StringName()) {
+ Vector2 to = (state_machine->get_node(snap_x)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
+ state_machine_draw->draw_line(from, to, linecolor, 2);
+ }
+ if (snap_y != StringName()) {
+ Vector2 to = (state_machine->get_node(snap_y)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
+ state_machine_draw->draw_line(from, to, linecolor, 2);
+ }
+ }
+
+ //pre pass nodes so we know the rectangles
+ for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
+
+ Ref<AnimationNode> anode = state_machine->get_node(E->get());
+ String name = E->get();
+ bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr());
+ Ref<StyleBox> sb = E->get() == selected_node ? style_selected : style;
+
+ Size2 s = sb->get_minimum_size();
+ int strsize = font->get_string_size(name).width;
+ s.width += strsize;
+ s.height += MAX(font->get_height(), play->get_height());
+ s.width += sep + play->get_width();
+ if (needs_editor) {
+ s.width += sep + edit->get_width();
+ }
+
+ Vector2 offset;
+ offset += anode->get_position() * EDSCALE;
+ if (selected_node == E->get() && dragging_selected) {
+ offset += drag_ofs;
+ }
+ offset -= s / 2;
+ offset = offset.floor();
+
+ //prepre rect
+
+ NodeRect nr;
+ nr.node = Rect2(offset, s);
+ nr.node_name = E->get();
+
+ scroll_range = scroll_range.merge(nr.node); //merge with range
+
+ //now scroll it to draw
+ nr.node.position -= state_machine->get_graph_offset() * EDSCALE;
+
+ node_rects.push_back(nr);
+ }
+
+ transition_lines.clear();
+
+ //draw conecting line for potential new transition
+ if (connecting) {
+ Vector2 from = (state_machine->get_node(connecting_from)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
+ Vector2 to;
+ if (connecting_to_node != StringName()) {
+ to = (state_machine->get_node(connecting_to_node)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
+ } else {
+ to = connecting_to;
+ }
+
+ for (int i = 0; i < node_rects.size(); i++) {
+ if (node_rects[i].node_name == connecting_from) {
+ _clip_src_line_to_rect(from, to, node_rects[i].node);
+ }
+ if (node_rects[i].node_name == connecting_to_node) {
+ _clip_dst_line_to_rect(from, to, node_rects[i].node);
+ }
+ }
+
+ _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false);
+ }
+
+ Ref<Texture> tr_reference_icon = get_icon("TransitionImmediateBig", "EditorIcons");
+ float tr_bidi_offset = int(tr_reference_icon->get_height() * 0.8);
+
+ //draw transition lines
+ for (int i = 0; i < state_machine->get_transition_count(); i++) {
+
+ TransitionLine tl;
+ tl.from_node = state_machine->get_transition_from(i);
+ Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2();
+ tl.from = (state_machine->get_node(tl.from_node)->get_position() * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE;
+
+ tl.to_node = state_machine->get_transition_to(i);
+ Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2();
+ tl.to = (state_machine->get_node(tl.to_node)->get_position() * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;
+
+ Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
+ tl.disabled = tr->is_disabled();
+ tl.auto_advance = tr->has_auto_advance();
+ tl.mode = tr->get_switch_mode();
+ tl.width = tr_bidi_offset;
+
+ if (state_machine->has_transition(tl.to_node, tl.from_node)) { //offset if same exists
+ Vector2 offset = -(tl.from - tl.to).normalized().tangent() * tr_bidi_offset;
+ tl.from += offset;
+ tl.to += offset;
+ }
+
+ for (int i = 0; i < node_rects.size(); i++) {
+ if (node_rects[i].node_name == tl.from_node) {
+ _clip_src_line_to_rect(tl.from, tl.to, node_rects[i].node);
+ }
+ if (node_rects[i].node_name == tl.to_node) {
+ _clip_dst_line_to_rect(tl.from, tl.to, node_rects[i].node);
+ }
+ }
+
+ bool selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node;
+
+ bool travel = false;
+
+ if (blend_from == tl.from_node && current == tl.to_node) {
+ travel = true;
+ }
+
+ if (travel_path.size()) {
+
+ if (current == tl.from_node && travel_path[0] == tl.to_node) {
+ travel = true;
+ } else {
+ for (int j = 0; j < travel_path.size() - 1; j++) {
+ if (travel_path[j] == tl.from_node && travel_path[j + 1] == tl.to_node) {
+ travel = true;
+ break;
+ }
+ }
+ }
+ }
+ _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, tl.auto_advance);
+
+ transition_lines.push_back(tl);
+ }
+
+ //draw actual nodes
+ for (int i = 0; i < node_rects.size(); i++) {
+
+ String name = node_rects[i].node_name;
+ Ref<AnimationNode> anode = state_machine->get_node(name);
+ bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr());
+ Ref<StyleBox> sb = name == selected_node ? style_selected : style;
+ int strsize = font->get_string_size(name).width;
+
+ NodeRect &nr = node_rects[i];
+
+ Vector2 offset = nr.node.position;
+ int h = nr.node.size.height;
+
+ //prepre rect
+
+ //now scroll it to draw
+ state_machine_draw->draw_style_box(sb, nr.node);
+
+ if (playing && (blend_from == name || current == name || travel_path.find(name) != -1)) {
+ state_machine_draw->draw_style_box(playing_overlay, nr.node);
+ }
+
+ bool onstart = state_machine->get_start_node() == name;
+ if (onstart) {
+ state_machine_draw->draw_string(font, offset + Vector2(0, -font->get_height() - 3 * EDSCALE + font->get_ascent()), TTR("Start"), font_color);
+ }
+
+ if (state_machine->get_end_node() == name) {
+
+ int endofs = nr.node.size.x - font->get_string_size(TTR("End")).x;
+ state_machine_draw->draw_string(font, offset + Vector2(endofs, -font->get_height() - 3 * EDSCALE + font->get_ascent()), TTR("End"), font_color);
+ }
+
+ offset.x += sb->get_offset().x;
+
+ nr.play.position = offset + Vector2(0, (h - play->get_height()) / 2).floor();
+ nr.play.size = play->get_size();
+
+ Ref<Texture> play_tex = onstart ? auto_play : play;
+
+ if (over_node == name && over_node_what == 0) {
+ state_machine_draw->draw_texture(play_tex, nr.play.position, accent);
+ } else {
+ state_machine_draw->draw_texture(play_tex, nr.play.position);
+ }
+ offset.x += sep + play->get_width();
+
+ nr.name.position = offset + Vector2(0, (h - font->get_height()) / 2).floor();
+ nr.name.size = Vector2(strsize, font->get_height());
+
+ state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent()), name, font_color);
+ offset.x += strsize + sep;
+
+ if (needs_editor) {
+ nr.edit.position = offset + Vector2(0, (h - edit->get_height()) / 2).floor();
+ nr.edit.size = edit->get_size();
+
+ if (over_node == name && over_node_what == 1) {
+ state_machine_draw->draw_texture(edit, nr.edit.position, accent);
+ } else {
+ state_machine_draw->draw_texture(edit, nr.edit.position);
+ }
+ offset.x += sep + edit->get_width();
+ }
+ }
+
+ scroll_range = scroll_range.grow(200 * EDSCALE);
+
+ //adjust scrollbars
+ updating = true;
+ h_scroll->set_min(scroll_range.position.x);
+ h_scroll->set_max(scroll_range.position.x + scroll_range.size.x);
+ h_scroll->set_page(state_machine_draw->get_size().x);
+ h_scroll->set_value(state_machine->get_graph_offset().x);
+
+ v_scroll->set_min(scroll_range.position.y);
+ v_scroll->set_max(scroll_range.position.y + scroll_range.size.y);
+ v_scroll->set_page(state_machine_draw->get_size().y);
+ v_scroll->set_value(state_machine->get_graph_offset().y);
+ updating = false;
+
+ state_machine_play_pos->update();
+}
+
+void AnimationNodeStateMachineEditor::_state_machine_pos_draw() {
+
+ if (!state_machine->is_playing())
+ return;
+
+ int idx = -1;
+ for (int i = 0; node_rects.size(); i++) {
+ if (node_rects[i].node_name == state_machine->get_current_node()) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1)
+ return;
+
+ NodeRect &nr = node_rects[idx];
+
+ Vector2 from;
+ from.x = nr.play.position.x;
+ from.y = (nr.play.position.y + nr.play.size.y + nr.node.position.y + nr.node.size.y) * 0.5;
+
+ Vector2 to;
+ if (nr.edit.size.x) {
+ to.x = nr.edit.position.x + nr.edit.size.x;
+ } else {
+ to.x = nr.name.position.x + nr.name.size.x;
+ }
+ to.y = from.y;
+
+ float len = MAX(0.0001, state_machine->get_current_length());
+
+ float pos = CLAMP(state_machine->get_current_play_pos(), 0, len);
+ float c = pos / len;
+ Color fg = get_color("font_color", "Label");
+ Color bg = fg;
+ bg.a *= 0.3;
+
+ state_machine_play_pos->draw_line(from, to, bg, 2);
+
+ to = from.linear_interpolate(to, c);
+
+ state_machine_play_pos->draw_line(from, to, fg, 2);
+}
+
+void AnimationNodeStateMachineEditor::_update_graph() {
+
+ if (updating)
+ return;
+
+ updating = true;
+
+ if (state_machine->get_parent().is_valid()) {
+ goto_parent_hbox->show();
+ } else {
+ goto_parent_hbox->hide();
+ }
+
+ state_machine_draw->update();
+
+ updating = false;
+}
+
+void AnimationNodeStateMachineEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ goto_parent->set_icon(get_icon("MoveUp", "EditorIcons"));
+
+ tool_select->set_icon(get_icon("ToolSelect", "EditorIcons"));
+ tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons"));
+ tool_connect->set_icon(get_icon("ToolConnect", "EditorIcons"));
+
+ transition_mode->clear();
+ transition_mode->add_icon_item(get_icon("TransitionImmediate", "EditorIcons"), TTR("Immediate"));
+ transition_mode->add_icon_item(get_icon("TransitionSync", "EditorIcons"), TTR("Sync"));
+ transition_mode->add_icon_item(get_icon("TransitionEnd", "EditorIcons"), TTR("At End"));
+
+ //force filter on those, so they deform better
+ get_icon("TransitionImmediateBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+ get_icon("TransitionEndBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+ get_icon("TransitionSyncBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+ get_icon("TransitionImmediateAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+ get_icon("TransitionEndAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+ get_icon("TransitionSyncAutoBig", "EditorIcons")->set_flags(Texture::FLAG_FILTER);
+
+ tool_erase->set_icon(get_icon("Remove", "EditorIcons"));
+ tool_autoplay->set_icon(get_icon("AutoPlay", "EditorIcons"));
+ tool_end->set_icon(get_icon("AutoEnd", "EditorIcons"));
+
+ play_mode->clear();
+ play_mode->add_icon_item(get_icon("PlayTravel", "EditorIcons"), TTR("Travel"));
+ play_mode->add_icon_item(get_icon("Play", "EditorIcons"), TTR("Immediate"));
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+
+ String error;
+
+ if (error_time > 0) {
+ error = error_text;
+ error_time -= get_process_delta_time();
+ } else if (!state_machine->get_tree()) {
+ error = TTR("StateMachine does not belong to an AnimationTree node.");
+ } else if (!state_machine->get_tree()->is_active()) {
+ error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
+ } else if (state_machine->get_tree()->is_state_invalid()) {
+ error = state_machine->get_tree()->get_invalid_state_reason();
+ } else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) {
+ if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) {
+ error = TTR("Start and end nodes are needed for a sub-transition.");
+ }
+ }
+
+ if (error != error_label->get_text()) {
+ error_label->set_text(error);
+ if (error != String()) {
+ error_panel->show();
+ } else {
+ error_panel->hide();
+ }
+ }
+
+ for (int i = 0; i < transition_lines.size(); i++) {
+ int tidx = -1;
+ for (int j = 0; j < state_machine->get_transition_count(); j++) {
+ if (transition_lines[i].from_node == state_machine->get_transition_from(j) && transition_lines[i].to_node == state_machine->get_transition_to(j)) {
+ tidx = j;
+ break;
+ }
+ }
+
+ if (tidx == -1) { //missing transition, should redraw
+ state_machine_draw->update();
+ break;
+ }
+
+ if (transition_lines[i].disabled != state_machine->get_transition(tidx)->is_disabled()) {
+ state_machine_draw->update();
+ break;
+ }
+
+ if (transition_lines[i].auto_advance != state_machine->get_transition(tidx)->has_auto_advance()) {
+ state_machine_draw->update();
+ break;
+ }
+
+ if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) {
+ state_machine_draw->update();
+ break;
+ }
+ }
+
+ bool same_travel_path = true;
+ Vector<StringName> tp = state_machine->get_travel_path();
+
+ {
+
+ if (last_travel_path.size() != tp.size()) {
+ same_travel_path = false;
+ } else {
+ for (int i = 0; i < last_travel_path.size(); i++) {
+ if (last_travel_path[i] != tp[i]) {
+ same_travel_path = false;
+ break;
+ }
+ }
+ }
+ }
+
+ //update if travel state changed
+ if (!same_travel_path || last_active != state_machine->is_playing() || last_current_node != state_machine->get_current_node() || last_blend_from_node != state_machine->get_blend_from_node()) {
+
+ state_machine_draw->update();
+ last_travel_path = tp;
+ last_current_node = state_machine->get_current_node();
+ last_active = state_machine->is_playing();
+ last_blend_from_node = state_machine->get_blend_from_node();
+ state_machine_play_pos->update();
+ }
+
+ if (last_play_pos != state_machine->get_current_play_pos()) {
+
+ last_play_pos = state_machine->get_current_play_pos();
+ state_machine_play_pos->update();
+ }
+ }
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ over_node = StringName();
+ }
+}
+
+void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) {
+ Ref<AnimationNode> an = state_machine->get_node(p_name);
+ ERR_FAIL_COND(!an.is_valid());
+ EditorNode::get_singleton()->edit_item(an.ptr());
+}
+
+void AnimationNodeStateMachineEditor::_goto_parent() {
+
+ EditorNode::get_singleton()->edit_item(state_machine->get_parent().ptr());
+}
+
+void AnimationNodeStateMachineEditor::_removed_from_graph() {
+ EditorNode::get_singleton()->edit_item(NULL);
+}
+
+void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
+
+ String new_name = p_text;
+
+ ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1)
+
+ ERR_FAIL_COND(new_name == prev_name);
+
+ String base_name = new_name;
+ int base = 1;
+ String name = base_name;
+ while (state_machine->has_node(name)) {
+ base++;
+ name = base_name + " " + itos(base);
+ }
+
+ updating = true;
+ undo_redo->create_action("Node Renamed");
+ undo_redo->add_do_method(state_machine.ptr(), "rename_node", prev_name, name);
+ undo_redo->add_undo_method(state_machine.ptr(), "rename_node", name, prev_name);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ state_machine_draw->update();
+
+ name_edit->hide();
+}
+
+void AnimationNodeStateMachineEditor::_scroll_changed(double) {
+ if (updating)
+ return;
+
+ state_machine->set_graph_offset(Vector2(h_scroll->get_value(), v_scroll->get_value()));
+ state_machine_draw->update();
+}
+
+void AnimationNodeStateMachineEditor::_erase_selected() {
+
+ if (selected_node != StringName() && state_machine->has_node(selected_node)) {
+ updating = true;
+ undo_redo->create_action("Node Removed");
+ undo_redo->add_do_method(state_machine.ptr(), "remove_node", selected_node);
+ undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node));
+ for (int i = 0; i < state_machine->get_transition_count(); i++) {
+ String from = state_machine->get_transition_from(i);
+ String to = state_machine->get_transition_to(i);
+ if (from == selected_node || to == selected_node) {
+ undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(i));
+ }
+ }
+ if (String(state_machine->get_start_node()) == selected_node) {
+ undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", selected_node);
+ }
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+ selected_node = StringName();
+ }
+
+ if (selected_transition_to != StringName() && selected_transition_from != StringName() && state_machine->has_transition(selected_transition_from, selected_transition_to)) {
+
+ Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to));
+ updating = true;
+ undo_redo->create_action("Transition Removed");
+ undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to);
+ undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+ selected_transition_from = StringName();
+ selected_transition_to = StringName();
+ }
+
+ state_machine_draw->update();
+}
+
+void AnimationNodeStateMachineEditor::_autoplay_selected() {
+
+ if (selected_node != StringName() && state_machine->has_node(selected_node)) {
+
+ StringName new_start_node;
+ if (state_machine->get_start_node() == selected_node) { //toggle it
+ new_start_node = StringName();
+ } else {
+ new_start_node = selected_node;
+ }
+
+ updating = true;
+ undo_redo->create_action("Set Start Node (Autoplay)");
+ undo_redo->add_do_method(state_machine.ptr(), "set_start_node", new_start_node);
+ undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", state_machine->get_start_node());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+ state_machine_draw->update();
+ }
+}
+
+void AnimationNodeStateMachineEditor::_end_selected() {
+
+ if (selected_node != StringName() && state_machine->has_node(selected_node)) {
+
+ StringName new_end_node;
+ if (state_machine->get_end_node() == selected_node) { //toggle it
+ new_end_node = StringName();
+ } else {
+ new_end_node = selected_node;
+ }
+
+ updating = true;
+ undo_redo->create_action("Set Start Node (Autoplay)");
+ undo_redo->add_do_method(state_machine.ptr(), "set_end_node", new_end_node);
+ undo_redo->add_undo_method(state_machine.ptr(), "set_end_node", state_machine->get_end_node());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+ state_machine_draw->update();
+ }
+}
+void AnimationNodeStateMachineEditor::_update_mode() {
+
+ if (tool_select->is_pressed()) {
+ tool_erase_hb->show();
+ tool_erase->set_disabled(selected_node == StringName() && selected_transition_from == StringName() && selected_transition_to == StringName());
+ tool_autoplay->set_disabled(selected_node == StringName());
+ tool_end->set_disabled(selected_node == StringName());
+ } else {
+ tool_erase_hb->hide();
+ }
+}
+
+void AnimationNodeStateMachineEditor::_bind_methods() {
+
+ ClassDB::bind_method("_state_machine_gui_input", &AnimationNodeStateMachineEditor::_state_machine_gui_input);
+ ClassDB::bind_method("_state_machine_draw", &AnimationNodeStateMachineEditor::_state_machine_draw);
+ ClassDB::bind_method("_state_machine_pos_draw", &AnimationNodeStateMachineEditor::_state_machine_pos_draw);
+ ClassDB::bind_method("_update_graph", &AnimationNodeStateMachineEditor::_update_graph);
+
+ ClassDB::bind_method("_add_menu_type", &AnimationNodeStateMachineEditor::_add_menu_type);
+ ClassDB::bind_method("_add_animation_type", &AnimationNodeStateMachineEditor::_add_animation_type);
+
+ ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited);
+
+ ClassDB::bind_method("_goto_parent", &AnimationNodeStateMachineEditor::_goto_parent);
+ ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph);
+
+ ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor);
+ ClassDB::bind_method("_scroll_changed", &AnimationNodeStateMachineEditor::_scroll_changed);
+
+ ClassDB::bind_method("_erase_selected", &AnimationNodeStateMachineEditor::_erase_selected);
+ ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected);
+ ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected);
+ ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode);
+}
+
+AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL;
+
+AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
+
+ singleton = this;
+ updating = false;
+
+ HBoxContainer *top_hb = memnew(HBoxContainer);
+ add_child(top_hb);
+
+ goto_parent_hbox = memnew(HBoxContainer);
+ goto_parent = memnew(ToolButton);
+ goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED);
+ goto_parent_hbox->add_child(goto_parent);
+ goto_parent_hbox->add_child(memnew(VSeparator));
+ top_hb->add_child(goto_parent_hbox);
+
+ Ref<ButtonGroup> bg;
+ bg.instance();
+
+ tool_select = memnew(ToolButton);
+ top_hb->add_child(tool_select);
+ tool_select->set_toggle_mode(true);
+ tool_select->set_button_group(bg);
+ tool_select->set_pressed(true);
+ tool_select->set_tooltip(TTR("Select and move nodes.\nRMB to add new nodes.\nShift+LMB to create connections."));
+ tool_select->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
+
+ tool_create = memnew(ToolButton);
+ top_hb->add_child(tool_create);
+ tool_create->set_toggle_mode(true);
+ tool_create->set_button_group(bg);
+ tool_create->set_tooltip(TTR("Create new nodes."));
+ tool_create->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
+
+ tool_connect = memnew(ToolButton);
+ top_hb->add_child(tool_connect);
+ tool_connect->set_toggle_mode(true);
+ tool_connect->set_button_group(bg);
+ tool_connect->set_tooltip(TTR("Connect nodes."));
+ tool_connect->connect("pressed", this, "_update_mode", varray(), CONNECT_DEFERRED);
+
+ tool_erase_hb = memnew(HBoxContainer);
+ top_hb->add_child(tool_erase_hb);
+ tool_erase_hb->add_child(memnew(VSeparator));
+ tool_erase = memnew(ToolButton);
+ tool_erase->set_tooltip(TTR("Remove selected node or transition"));
+ tool_erase_hb->add_child(tool_erase);
+ tool_erase->connect("pressed", this, "_erase_selected");
+ tool_erase->set_disabled(true);
+
+ tool_erase_hb->add_child(memnew(VSeparator));
+
+ tool_autoplay = memnew(ToolButton);
+ tool_autoplay->set_tooltip(TTR("Toggle autoplay this animation on start, restart or seek to zero."));
+ tool_erase_hb->add_child(tool_autoplay);
+ tool_autoplay->connect("pressed", this, "_autoplay_selected");
+ tool_autoplay->set_disabled(true);
+
+ tool_end = memnew(ToolButton);
+ tool_end->set_tooltip(TTR("Set the end animation. This is useful for sub-transitions."));
+ tool_erase_hb->add_child(tool_end);
+ tool_end->connect("pressed", this, "_end_selected");
+ tool_end->set_disabled(true);
+
+ top_hb->add_child(memnew(VSeparator));
+ top_hb->add_child(memnew(Label(TTR("Transition: "))));
+ transition_mode = memnew(OptionButton);
+ top_hb->add_child(transition_mode);
+
+ top_hb->add_spacer();
+
+ top_hb->add_child(memnew(Label("Play Mode:")));
+ play_mode = memnew(OptionButton);
+ top_hb->add_child(play_mode);
+
+ GridContainer *main_grid = memnew(GridContainer);
+ main_grid->set_columns(2);
+ add_child(main_grid);
+ main_grid->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ panel = memnew(PanelContainer);
+ panel->set_clip_contents(true);
+ main_grid->add_child(panel);
+ panel->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ state_machine_draw = memnew(Control);
+ state_machine_draw->connect("gui_input", this, "_state_machine_gui_input");
+ state_machine_draw->connect("draw", this, "_state_machine_draw");
+ state_machine_draw->set_focus_mode(FOCUS_ALL);
+
+ state_machine_play_pos = memnew(Control);
+ state_machine_draw->add_child(state_machine_play_pos);
+ state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent
+ state_machine_play_pos->set_anchors_and_margins_preset(PRESET_WIDE);
+ state_machine_play_pos->connect("draw", this, "_state_machine_pos_draw");
+
+ panel->add_child(state_machine_draw);
+ panel->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ v_scroll = memnew(VScrollBar);
+ main_grid->add_child(v_scroll);
+ v_scroll->connect("value_changed", this, "_scroll_changed");
+
+ h_scroll = memnew(HScrollBar);
+ main_grid->add_child(h_scroll);
+ h_scroll->connect("value_changed", this, "_scroll_changed");
+
+ main_grid->add_child(memnew(Control)); //empty bottom right
+
+ error_panel = memnew(PanelContainer);
+ add_child(error_panel);
+ error_label = memnew(Label);
+ error_panel->add_child(error_label);
+ error_label->set_text("eh");
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ set_custom_minimum_size(Size2(0, 300 * EDSCALE));
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("index_pressed", this, "_add_menu_type");
+
+ animations_menu = memnew(PopupMenu);
+ menu->add_child(animations_menu);
+ animations_menu->set_name("animations");
+ animations_menu->connect("index_pressed", this, "_add_animation_type");
+
+ name_edit = memnew(LineEdit);
+ state_machine_draw->add_child(name_edit);
+ name_edit->hide();
+ name_edit->connect("text_entered", this, "_name_edited");
+ name_edit->set_as_toplevel(true);
+
+ over_text = false;
+
+ over_node_what = -1;
+ dragging_selected_attempt = false;
+ connecting = false;
+
+ last_active = false;
+
+ error_time = 0;
+}
+
+void AnimationNodeStateMachineEditorPlugin::edit(Object *p_object) {
+
+ anim_tree_editor->edit(Object::cast_to<AnimationNodeStateMachine>(p_object));
+}
+
+bool AnimationNodeStateMachineEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("AnimationNodeStateMachine");
+}
+
+void AnimationNodeStateMachineEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ //editor->hide_animation_player_editors();
+ //editor->animation_panel_make_visible(true);
+ button->show();
+ editor->make_bottom_panel_item_visible(anim_tree_editor);
+ anim_tree_editor->set_process(true);
+ } else {
+
+ if (anim_tree_editor->is_visible_in_tree())
+ editor->hide_bottom_panel();
+ button->hide();
+ anim_tree_editor->set_process(false);
+ }
+}
+
+AnimationNodeStateMachineEditorPlugin::AnimationNodeStateMachineEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ anim_tree_editor = memnew(AnimationNodeStateMachineEditor);
+ anim_tree_editor->set_custom_minimum_size(Size2(0, 300));
+
+ button = editor->add_bottom_panel_item(TTR("StateMachine"), anim_tree_editor);
+ button->hide();
+}
+
+AnimationNodeStateMachineEditorPlugin::~AnimationNodeStateMachineEditorPlugin() {
+}
diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h
new file mode 100644
index 0000000000..efd3de7415
--- /dev/null
+++ b/editor/plugins/animation_state_machine_editor.h
@@ -0,0 +1,167 @@
+#ifndef ANIMATION_STATE_MACHINE_EDITOR_H
+#define ANIMATION_STATE_MACHINE_EDITOR_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/property_editor.h"
+#include "scene/animation/animation_node_state_machine.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+
+class AnimationNodeStateMachineEditor : public VBoxContainer {
+
+ GDCLASS(AnimationNodeStateMachineEditor, VBoxContainer);
+
+ Ref<AnimationNodeStateMachine> state_machine;
+
+ ToolButton *tool_select;
+ ToolButton *tool_create;
+ ToolButton *tool_connect;
+ LineEdit *name_edit;
+
+ HBoxContainer *tool_erase_hb;
+ ToolButton *tool_erase;
+ ToolButton *tool_autoplay;
+ ToolButton *tool_end;
+
+ OptionButton *transition_mode;
+ OptionButton *play_mode;
+
+ HBoxContainer *goto_parent_hbox;
+ ToolButton *goto_parent;
+
+ PanelContainer *panel;
+
+ StringName selected_node;
+
+ HScrollBar *h_scroll;
+ VScrollBar *v_scroll;
+
+ Control *state_machine_draw;
+ Control *state_machine_play_pos;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ bool updating;
+
+ UndoRedo *undo_redo;
+
+ static AnimationNodeStateMachineEditor *singleton;
+
+ void _state_machine_gui_input(const Ref<InputEvent> &p_event);
+ void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance);
+ void _state_machine_draw();
+ void _state_machine_pos_draw();
+
+ void _update_graph();
+
+ PopupMenu *menu;
+ PopupMenu *animations_menu;
+ Vector<String> animations_to_add;
+
+ Vector2 add_node_pos;
+
+ bool dragging_selected_attempt;
+ bool dragging_selected;
+ Vector2 drag_from;
+ Vector2 drag_ofs;
+ StringName snap_x;
+ StringName snap_y;
+
+ bool connecting;
+ StringName connecting_from;
+ Vector2 connecting_to;
+ StringName connecting_to_node;
+
+ void _add_menu_type(int p_index);
+ void _add_animation_type(int p_index);
+
+ void _goto_parent();
+
+ void _removed_from_graph();
+
+ struct NodeRect {
+ StringName node_name;
+ Rect2 node;
+ Rect2 play;
+ Rect2 name;
+ Rect2 edit;
+ };
+
+ Vector<NodeRect> node_rects;
+
+ struct TransitionLine {
+ StringName from_node;
+ StringName to_node;
+ Vector2 from;
+ Vector2 to;
+ AnimationNodeStateMachineTransition::SwitchMode mode;
+ bool disabled;
+ bool auto_advance;
+ float width;
+ };
+
+ Vector<TransitionLine> transition_lines;
+
+ StringName selected_transition_from;
+ StringName selected_transition_to;
+
+ bool over_text;
+ StringName over_node;
+ int over_node_what;
+
+ String prev_name;
+ void _name_edited(const String &p_text);
+ void _open_editor(const String &p_name);
+ void _scroll_changed(double);
+
+ void _clip_src_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect);
+ void _clip_dst_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect);
+
+ void _erase_selected();
+ void _update_mode();
+ void _autoplay_selected();
+ void _end_selected();
+
+ bool last_active;
+ StringName last_blend_from_node;
+ StringName last_current_node;
+ Vector<StringName> last_travel_path;
+ float last_play_pos;
+
+ float error_time;
+ String error_text;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static AnimationNodeStateMachineEditor *get_singleton() { return singleton; }
+ void edit(AnimationNodeStateMachine *p_state_machine);
+ AnimationNodeStateMachineEditor();
+};
+
+class AnimationNodeStateMachineEditorPlugin : public EditorPlugin {
+
+ GDCLASS(AnimationNodeStateMachineEditorPlugin, EditorPlugin);
+
+ AnimationNodeStateMachineEditor *anim_tree_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ virtual String get_name() const { return "StateMachine"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ AnimationNodeStateMachineEditorPlugin(EditorNode *p_node);
+ ~AnimationNodeStateMachineEditorPlugin();
+};
+
+#endif // ANIMATION_STATE_MACHINE_EDITOR_H
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
new file mode 100644
index 0000000000..ddb03d0250
--- /dev/null
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -0,0 +1,284 @@
+/*************************************************************************/
+/* audio_stream_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "audio_stream_editor_plugin.h"
+
+#include "editor/editor_settings.h"
+#include "io/resource_loader.h"
+#include "project_settings.h"
+
+void AudioStreamEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_READY) {
+ AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", this, "_preview_changed");
+ }
+
+ if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ _stop_button->set_icon(get_icon("Stop", "EditorIcons"));
+ _preview->set_frame_color(get_color("dark_color_2", "Editor"));
+ set_frame_color(get_color("dark_color_1", "Editor"));
+
+ _indicator->update();
+ _preview->update();
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+ _current = _player->get_playback_position();
+ _indicator->update();
+ _preview->update();
+ }
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (!is_visible_in_tree()) {
+ _stop();
+ }
+ }
+}
+
+void AudioStreamEditor::_draw_preview() {
+ Rect2 rect = _preview->get_rect();
+ Size2 size = get_size();
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+ float preview_len = preview->get_length();
+
+ Vector<Vector2> lines;
+ lines.resize(size.width * 2);
+
+ for (int i = 0; i < size.width; i++) {
+
+ float ofs = i * preview_len / size.width;
+ float ofs_n = (i + 1) * preview_len / size.width;
+ float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
+ float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
+
+ int idx = i;
+ lines[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y);
+ lines[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y);
+ }
+
+ Vector<Color> color;
+ color.push_back(get_color("contrast_color_2", "Editor"));
+
+ VS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), lines, color);
+}
+
+void AudioStreamEditor::_preview_changed(ObjectID p_which) {
+
+ if (stream.is_valid() && stream->get_instance_id() == p_which) {
+ _preview->update();
+ }
+}
+
+void AudioStreamEditor::_changed_callback(Object *p_changed, const char *p_prop) {
+
+ if (!is_visible())
+ return;
+ update();
+}
+
+void AudioStreamEditor::_play() {
+
+ if (_player->is_playing()) {
+ _player->stop();
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ set_process(false);
+ } else {
+ _player->play(_current);
+ _play_button->set_icon(get_icon("Pause", "EditorIcons"));
+ set_process(true);
+ }
+}
+
+void AudioStreamEditor::_stop() {
+
+ _player->stop();
+ _on_finished();
+}
+
+void AudioStreamEditor::_on_finished() {
+
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ _current = 0;
+ _indicator->update();
+ set_process(false);
+}
+
+void AudioStreamEditor::_draw_indicator() {
+
+ if (!stream.is_valid()) {
+ return;
+ }
+
+ Rect2 rect = _preview->get_rect();
+ float len = stream->get_length();
+ float ofs_x = _current / len * rect.size.width;
+ _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), get_color("accent_color", "Editor"), 1);
+
+ _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
+}
+
+void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid()) {
+ if (mb->is_pressed()) {
+ _seek_to(mb->get_position().x);
+ }
+ _dragging = mb->is_pressed();
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+ if (_dragging) {
+ _seek_to(mm->get_position().x);
+ }
+ }
+}
+
+void AudioStreamEditor::_seek_to(real_t p_x) {
+ _current = p_x / _preview->get_rect().size.x * stream->get_length();
+ _current = CLAMP(_current, 0, stream->get_length());
+ _player->seek(_current);
+ _indicator->update();
+}
+
+void AudioStreamEditor::edit(Ref<AudioStream> p_stream) {
+
+ if (!stream.is_null())
+ stream->remove_change_receptor(this);
+
+ stream = p_stream;
+ _player->set_stream(stream);
+ _current = 0;
+ String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s";
+ _duration_label->set_text(text);
+
+ if (!stream.is_null()) {
+ stream->add_change_receptor(this);
+ update();
+ } else {
+ hide();
+ }
+}
+
+void AudioStreamEditor::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_preview_changed"), &AudioStreamEditor::_preview_changed);
+ ClassDB::bind_method(D_METHOD("_play"), &AudioStreamEditor::_play);
+ ClassDB::bind_method(D_METHOD("_stop"), &AudioStreamEditor::_stop);
+ ClassDB::bind_method(D_METHOD("_on_finished"), &AudioStreamEditor::_on_finished);
+ ClassDB::bind_method(D_METHOD("_draw_preview"), &AudioStreamEditor::_draw_preview);
+ ClassDB::bind_method(D_METHOD("_draw_indicator"), &AudioStreamEditor::_draw_indicator);
+ ClassDB::bind_method(D_METHOD("_on_input_indicator"), &AudioStreamEditor::_on_input_indicator);
+}
+
+AudioStreamEditor::AudioStreamEditor() {
+
+ set_custom_minimum_size(Size2(1, 100));
+ _current = 0;
+ _dragging = false;
+
+ _player = memnew(AudioStreamPlayer);
+ _player->connect("finished", this, "_on_finished");
+ add_child(_player);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ vbox->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_MINSIZE, 0);
+ add_child(vbox);
+
+ _preview = memnew(ColorRect);
+ _preview->set_v_size_flags(SIZE_EXPAND_FILL);
+ _preview->connect("draw", this, "_draw_preview");
+ vbox->add_child(_preview);
+
+ _indicator = memnew(Control);
+ _indicator->set_anchors_and_margins_preset(PRESET_WIDE);
+ _indicator->connect("draw", this, "_draw_indicator");
+ _indicator->connect("gui_input", this, "_on_input_indicator");
+ _preview->add_child(_indicator);
+
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->add_constant_override("separation", 0);
+ vbox->add_child(hbox);
+
+ _play_button = memnew(ToolButton);
+ hbox->add_child(_play_button);
+ _play_button->set_focus_mode(Control::FOCUS_NONE);
+ _play_button->connect("pressed", this, "_play");
+
+ _stop_button = memnew(ToolButton);
+ hbox->add_child(_stop_button);
+ _stop_button->set_focus_mode(Control::FOCUS_NONE);
+ _stop_button->connect("pressed", this, "_stop");
+
+ _current_label = memnew(Label);
+ _current_label->set_align(Label::ALIGN_RIGHT);
+ _current_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ _current_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts"));
+ _current_label->set_modulate(Color(1, 1, 1, 0.5));
+ hbox->add_child(_current_label);
+
+ _duration_label = memnew(Label);
+ _duration_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts"));
+ hbox->add_child(_duration_label);
+}
+
+void AudioStreamEditorPlugin::edit(Object *p_object) {
+
+ AudioStream *s = Object::cast_to<AudioStream>(p_object);
+ if (!s)
+ return;
+
+ audio_editor->edit(Ref<AudioStream>(s));
+}
+
+bool AudioStreamEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("AudioStream");
+}
+
+void AudioStreamEditorPlugin::make_visible(bool p_visible) {
+
+ audio_editor->set_visible(p_visible);
+}
+
+AudioStreamEditorPlugin::AudioStreamEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ audio_editor = memnew(AudioStreamEditor);
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, audio_editor);
+ audio_editor->hide();
+}
+
+AudioStreamEditorPlugin::~AudioStreamEditorPlugin() {
+}
diff --git a/modules/theora/resource_importer_theora.cpp b/editor/plugins/audio_stream_editor_plugin.h
index ee9bab74a7..1887874b74 100644
--- a/modules/theora/resource_importer_theora.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_theora.cpp */
+/* audio_stream_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,63 +28,66 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_theora.h"
+#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H
+#define AUDIO_STREAM_EDITOR_PLUGIN_H
-#include "io/resource_saver.h"
-#include "os/file_access.h"
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/audio/audio_player.h"
+#include "scene/gui/color_rect.h"
#include "scene/resources/texture.h"
-String ResourceImporterTheora::get_importer_name() const {
+class AudioStreamEditor : public ColorRect {
- return "Theora";
-}
+ GDCLASS(AudioStreamEditor, ColorRect);
-String ResourceImporterTheora::get_visible_name() const {
+ Ref<AudioStream> stream;
+ AudioStreamPlayer *_player;
+ ColorRect *_preview;
+ Control *_indicator;
+ Label *_current_label;
+ Label *_duration_label;
- return "Theora";
-}
-void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const {
+ ToolButton *_play_button;
+ ToolButton *_stop_button;
- p_extensions->push_back("ogv");
- p_extensions->push_back("ogm");
-}
+ float _current;
+ bool _dragging;
-String ResourceImporterTheora::get_save_extension() const {
- return "ogvstr";
-}
+protected:
+ void _notification(int p_what);
+ void _preview_changed(ObjectID p_which);
+ void _play();
+ void _stop();
+ void _on_finished();
+ void _draw_preview();
+ void _draw_indicator();
+ void _on_input_indicator(Ref<InputEvent> p_event);
+ void _seek_to(real_t p_x);
+ void _changed_callback(Object *p_changed, const char *p_prop);
+ static void _bind_methods();
-String ResourceImporterTheora::get_resource_type() const {
+public:
+ void edit(Ref<AudioStream> p_stream);
+ AudioStreamEditor();
+};
- return "VideoStreamTheora";
-}
+class AudioStreamEditorPlugin : public EditorPlugin {
-bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ GDCLASS(AudioStreamEditorPlugin, EditorPlugin);
- return true;
-}
+ AudioStreamEditor *audio_editor;
+ EditorNode *editor;
-int ResourceImporterTheora::get_preset_count() const {
- return 0;
-}
-String ResourceImporterTheora::get_preset_name(int p_idx) const {
+public:
+ virtual String get_name() const { return "Audio"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
- return String();
-}
+ AudioStreamEditorPlugin(EditorNode *p_node);
+ ~AudioStreamEditorPlugin();
+};
-void ResourceImporterTheora::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
-}
-
-Error ResourceImporterTheora::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) {
-
- VideoStreamTheora *stream = memnew(VideoStreamTheora);
- stream->set_file(p_source_file);
-
- Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
-
- return ResourceSaver::save(p_save_path + ".ogvstr", ogv_stream);
-}
-
-ResourceImporterTheora::ResourceImporterTheora() {
-}
+#endif // AUDIO_STREAM_EDITOR_PLUGIN_H
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 0f46f7f004..1d20c63969 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -543,7 +543,6 @@ void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResu
for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
- Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to));
Vector<Vector2> bone_shape;
if (!_get_bone_shape(&bone_shape, NULL, E))
@@ -719,16 +718,17 @@ Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2
ERR_FAIL_COND_V(!p_control, Vector2());
Transform2D parent_transform = p_control->get_transform().affine_inverse();
- Size2 parent_size = p_control->get_parent_area_size();
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
- return parent_transform.xform(Vector2(parent_size.x * anchor.x, parent_size.y * anchor.y));
+ return parent_transform.xform(parent_rect.position + Vector2(parent_rect.size.x * anchor.x, parent_rect.size.y * anchor.y));
}
Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 position) {
ERR_FAIL_COND_V(!p_control, Vector2());
- Size2 parent_size = p_control->get_parent_area_size();
- return p_control->get_transform().xform(position) / parent_size;
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
+
+ return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size;
}
void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) {
@@ -2491,10 +2491,12 @@ void CanvasItemEditor::_draw_selection() {
Transform2D parent_transform = xform * control->get_transform().affine_inverse();
float node_pos_in_parent[4];
- node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * control->get_parent_area_size().width + control->get_margin(MARGIN_LEFT);
- node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * control->get_parent_area_size().height + control->get_margin(MARGIN_TOP);
- node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * control->get_parent_area_size().width + control->get_margin(MARGIN_RIGHT);
- node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * control->get_parent_area_size().height + control->get_margin(MARGIN_BOTTOM);
+ Rect2 parent_rect = control->get_parent_anchorable_rect();
+
+ node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x;
+ node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y;
+ node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x;
+ node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y;
Point2 start, end;
switch (drag_type) {
@@ -4351,7 +4353,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_button->set_toggle_mode(true);
snap_button->connect("toggled", this, "_button_toggle_snap");
snap_button->set_tooltip(TTR("Toggle snapping."));
- snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_S));
+ snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_MASK_SHIFT | KEY_S));
snap_config_menu = memnew(MenuButton);
hb->add_child(snap_config_menu);
@@ -4479,7 +4481,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_insert_button->set_flat(true);
key_insert_button->set_focus_mode(FOCUS_NONE);
key_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY));
- key_insert_button->set_tooltip(TTR("Insert Keys"));
+ key_insert_button->set_tooltip(TTR("Insert keys."));
key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT));
animation_hb->add_child(key_insert_button);
diff --git a/editor/plugins/cpu_particles_editor_plugin.cpp b/editor/plugins/cpu_particles_editor_plugin.cpp
new file mode 100644
index 0000000000..b32f927249
--- /dev/null
+++ b/editor/plugins/cpu_particles_editor_plugin.cpp
@@ -0,0 +1,114 @@
+#include "cpu_particles_editor_plugin.h"
+#include "editor/plugins/spatial_editor_plugin.h"
+
+void CPUParticlesEditor::_node_removed(Node *p_node) {
+
+ if (p_node == node) {
+ node = NULL;
+ hide();
+ }
+}
+
+void CPUParticlesEditor::_notification(int p_notification) {
+
+ if (p_notification == NOTIFICATION_ENTER_TREE) {
+ options->set_icon(options->get_popup()->get_icon("CPUParticles", "EditorIcons"));
+ }
+}
+
+void CPUParticlesEditor::_menu_option(int p_option) {
+
+ switch (p_option) {
+
+ case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: {
+
+ emission_file_dialog->popup_centered_ratio();
+
+ } break;
+
+ case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
+
+ emission_tree_dialog->popup_centered_ratio();
+
+ } break;
+ }
+}
+
+void CPUParticlesEditor::edit(CPUParticles *p_particles) {
+
+ base_node = p_particles;
+ node = p_particles;
+}
+
+void CPUParticlesEditor::_generate_emission_points() {
+
+ /// hacer codigo aca
+ PoolVector<Vector3> points;
+ PoolVector<Vector3> normals;
+
+ if (!_generate(points, normals)) {
+ return;
+ }
+
+ if (normals.size() == 0) {
+ node->set_emission_shape(CPUParticles::EMISSION_SHAPE_POINTS);
+ node->set_emission_points(points);
+ } else {
+ node->set_emission_shape(CPUParticles::EMISSION_SHAPE_DIRECTED_POINTS);
+ node->set_emission_points(points);
+ node->set_emission_normals(normals);
+ }
+}
+
+void CPUParticlesEditor::_bind_methods() {
+
+ ClassDB::bind_method("_menu_option", &CPUParticlesEditor::_menu_option);
+}
+
+CPUParticlesEditor::CPUParticlesEditor() {
+
+ particles_editor_hb = memnew(HBoxContainer);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
+ options = memnew(MenuButton);
+ particles_editor_hb->add_child(options);
+ particles_editor_hb->hide();
+
+ options->set_text(TTR("CPUParticles"));
+ options->get_popup()->add_item(TTR("Create Emission Points From Mesh"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH);
+ options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
+ options->get_popup()->connect("id_pressed", this, "_menu_option");
+}
+
+void CPUParticlesEditorPlugin::edit(Object *p_object) {
+
+ particles_editor->edit(Object::cast_to<CPUParticles>(p_object));
+}
+
+bool CPUParticlesEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("CPUParticles");
+}
+
+void CPUParticlesEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ particles_editor->show();
+ particles_editor->particles_editor_hb->show();
+ } else {
+ particles_editor->particles_editor_hb->hide();
+ particles_editor->hide();
+ particles_editor->edit(NULL);
+ }
+}
+
+CPUParticlesEditorPlugin::CPUParticlesEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ particles_editor = memnew(CPUParticlesEditor);
+ editor->get_viewport()->add_child(particles_editor);
+
+ particles_editor->hide();
+}
+
+CPUParticlesEditorPlugin::~CPUParticlesEditorPlugin() {
+}
diff --git a/editor/plugins/cpu_particles_editor_plugin.h b/editor/plugins/cpu_particles_editor_plugin.h
new file mode 100644
index 0000000000..f47d17104d
--- /dev/null
+++ b/editor/plugins/cpu_particles_editor_plugin.h
@@ -0,0 +1,55 @@
+#ifndef CPU_PARTICLES_EDITOR_PLUGIN_H
+#define CPU_PARTICLES_EDITOR_PLUGIN_H
+
+#include "editor/plugins/particles_editor_plugin.h"
+#include "scene/3d/cpu_particles.h"
+
+class CPUParticlesEditor : public ParticlesEditorBase {
+
+ GDCLASS(CPUParticlesEditor, ParticlesEditorBase);
+
+ enum Menu {
+
+ MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
+ MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH,
+ MENU_OPTION_CLEAR_EMISSION_VOLUME,
+
+ };
+
+ CPUParticles *node;
+
+ void _menu_option(int);
+
+ friend class CPUParticlesEditorPlugin;
+
+ virtual void _generate_emission_points();
+
+protected:
+ void _notification(int p_notification);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+public:
+ void edit(CPUParticles *p_particles);
+ CPUParticlesEditor();
+};
+
+class CPUParticlesEditorPlugin : public EditorPlugin {
+
+ GDCLASS(CPUParticlesEditorPlugin, EditorPlugin);
+
+ CPUParticlesEditor *particles_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const { return "CPUParticles"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ CPUParticlesEditorPlugin(EditorNode *p_node);
+ ~CPUParticlesEditorPlugin();
+};
+
+#endif // CPU_PARTICLES_EDITOR_PLUGIN_H
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index 7728995a99..1f5a4a8a36 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -31,16 +31,136 @@
#include "particles_editor_plugin.h"
#include "editor/plugins/spatial_editor_plugin.h"
#include "io/resource_loader.h"
+#include "scene/3d/cpu_particles.h"
+bool ParticlesEditorBase::_generate(PoolVector<Vector3> &points, PoolVector<Vector3> &normals) {
-void ParticlesEditor::_node_removed(Node *p_node) {
+ bool use_normals = emission_fill->get_selected() == 1;
- if (p_node == node) {
- node = NULL;
- hide();
+ if (emission_fill->get_selected() < 2) {
+
+ float area_accum = 0;
+ Map<float, int> triangle_area_map;
+
+ for (int i = 0; i < geometry.size(); i++) {
+
+ float area = geometry[i].get_area();
+ if (area < CMP_EPSILON)
+ continue;
+ triangle_area_map[area_accum] = i;
+ area_accum += area;
+ }
+
+ if (!triangle_area_map.size() || area_accum == 0) {
+
+ err_dialog->set_text(TTR("Faces contain no area!"));
+ err_dialog->popup_centered_minsize();
+ return false;
+ }
+
+ int emissor_count = emission_amount->get_value();
+
+ for (int i = 0; i < emissor_count; i++) {
+
+ float areapos = Math::random(0.0f, area_accum);
+
+ Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
+ ERR_FAIL_COND_V(!E, false)
+ int index = E->get();
+ ERR_FAIL_INDEX_V(index, geometry.size(), false);
+
+ // ok FINALLY get face
+ Face3 face = geometry[index];
+ //now compute some position inside the face...
+
+ Vector3 pos = face.get_random_point_inside();
+
+ points.push_back(pos);
+
+ if (use_normals) {
+ Vector3 normal = face.get_plane().normal;
+ normals.push_back(normal);
+ }
+ }
+ } else {
+
+ int gcount = geometry.size();
+
+ if (gcount == 0) {
+
+ err_dialog->set_text(TTR("No faces!"));
+ err_dialog->popup_centered_minsize();
+ return false;
+ }
+
+ PoolVector<Face3>::Read r = geometry.read();
+
+ AABB aabb;
+
+ for (int i = 0; i < gcount; i++) {
+
+ for (int j = 0; j < 3; j++) {
+
+ if (i == 0 && j == 0)
+ aabb.position = r[i].vertex[j];
+ else
+ aabb.expand_to(r[i].vertex[j]);
+ }
+ }
+
+ int emissor_count = emission_amount->get_value();
+
+ for (int i = 0; i < emissor_count; i++) {
+
+ int attempts = 5;
+
+ for (int j = 0; j < attempts; j++) {
+
+ Vector3 dir;
+ dir[Math::rand() % 3] = 1.0;
+ Vector3 ofs = (Vector3(1, 1, 1) - dir) * Vector3(Math::randf(), Math::randf(), Math::randf()) * aabb.size + aabb.position;
+
+ Vector3 ofsv = ofs + aabb.size * dir;
+
+ //space it a little
+ ofs -= dir;
+ ofsv += dir;
+
+ float max = -1e7, min = 1e7;
+
+ for (int k = 0; k < gcount; k++) {
+
+ const Face3 &f3 = r[k];
+
+ Vector3 res;
+ if (f3.intersects_segment(ofs, ofsv, &res)) {
+
+ res -= ofs;
+ float d = dir.dot(res);
+
+ if (d < min)
+ min = d;
+ if (d > max)
+ max = d;
+ }
+ }
+
+ if (max < min)
+ continue; //lost attempt
+
+ float val = min + (max - min) * Math::randf();
+
+ Vector3 point = ofs + dir * val;
+
+ points.push_back(point);
+ break;
+ }
+ }
}
+
+ return true;
}
-void ParticlesEditor::_node_selected(const NodePath &p_path) {
+void ParticlesEditorBase::_node_selected(const NodePath &p_path) {
Node *sel = get_node(p_path);
if (!sel)
@@ -63,7 +183,7 @@ void ParticlesEditor::_node_selected(const NodePath &p_path) {
return;
}
- Transform geom_xform = node->get_global_transform().affine_inverse() * vi->get_global_transform();
+ Transform geom_xform = base_node->get_global_transform().affine_inverse() * vi->get_global_transform();
int gc = geometry.size();
PoolVector<Face3>::Write w = geometry.write();
@@ -79,6 +199,65 @@ void ParticlesEditor::_node_selected(const NodePath &p_path) {
emission_dialog->popup_centered(Size2(300, 130));
}
+void ParticlesEditorBase::_bind_methods() {
+
+ ClassDB::bind_method("_node_selected", &ParticlesEditorBase::_node_selected);
+ ClassDB::bind_method("_generate_emission_points", &ParticlesEditorBase::_generate_emission_points);
+}
+
+ParticlesEditorBase::ParticlesEditorBase() {
+
+ emission_dialog = memnew(ConfirmationDialog);
+ emission_dialog->set_title(TTR("Create Emitter"));
+ add_child(emission_dialog);
+ VBoxContainer *emd_vb = memnew(VBoxContainer);
+ emission_dialog->add_child(emd_vb);
+
+ emission_amount = memnew(SpinBox);
+ emission_amount->set_min(1);
+ emission_amount->set_max(100000);
+ emission_amount->set_value(512);
+ emd_vb->add_margin_child(TTR("Emission Points:"), emission_amount);
+
+ emission_fill = memnew(OptionButton);
+ emission_fill->add_item(TTR("Surface Points"));
+ emission_fill->add_item(TTR("Surface Points+Normal (Directed)"));
+ emission_fill->add_item(TTR("Volume"));
+ emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill);
+
+ emission_dialog->get_ok()->set_text(TTR("Create"));
+ emission_dialog->connect("confirmed", this, "_generate_emission_points");
+
+ err_dialog = memnew(ConfirmationDialog);
+ add_child(err_dialog);
+
+ emission_file_dialog = memnew(EditorFileDialog);
+ add_child(emission_file_dialog);
+ emission_file_dialog->connect("file_selected", this, "_resource_seleted");
+ emission_tree_dialog = memnew(SceneTreeDialog);
+ add_child(emission_tree_dialog);
+ emission_tree_dialog->connect("selected", this, "_node_selected");
+
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Mesh", &extensions);
+
+ emission_file_dialog->clear_filters();
+ for (int i = 0; i < extensions.size(); i++) {
+
+ emission_file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+ }
+
+ emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+}
+
+void ParticlesEditor::_node_removed(Node *p_node) {
+
+ if (p_node == node) {
+ node = NULL;
+ hide();
+ }
+}
+
void ParticlesEditor::_notification(int p_notification) {
if (p_notification == NOTIFICATION_ENTER_TREE) {
@@ -114,6 +293,25 @@ void ParticlesEditor::_menu_option(int p_option) {
emission_tree_dialog->popup_centered_ratio();
} break;
+ case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: {
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ CPUParticles *cpu_particles = memnew(CPUParticles);
+ cpu_particles->convert_from_particles(node);
+ cpu_particles->set_name(node->get_name());
+ cpu_particles->set_transform(node->get_transform());
+ cpu_particles->set_visible(node->is_visible());
+ cpu_particles->set_pause_mode(node->get_pause_mode());
+
+ undo_redo->create_action("Replace Particles by CPUParticles");
+ undo_redo->add_do_method(node, "replace_by", cpu_particles);
+ undo_redo->add_undo_method(cpu_particles, "replace_by", node);
+ undo_redo->add_do_reference(cpu_particles);
+ undo_redo->add_undo_reference(node);
+ undo_redo->commit_action();
+
+ } break;
}
}
@@ -146,145 +344,21 @@ void ParticlesEditor::_generate_aabb() {
void ParticlesEditor::edit(Particles *p_particles) {
+ base_node = p_particles;
node = p_particles;
}
void ParticlesEditor::_generate_emission_points() {
/// hacer codigo aca
- PoolVector<float> points;
- bool use_normals = emission_fill->get_selected() == 1;
- PoolVector<float> normals;
-
- if (emission_fill->get_selected() < 2) {
-
- float area_accum = 0;
- Map<float, int> triangle_area_map;
- print_line("geometry size: " + itos(geometry.size()));
-
- for (int i = 0; i < geometry.size(); i++) {
-
- float area = geometry[i].get_area();
- if (area < CMP_EPSILON)
- continue;
- triangle_area_map[area_accum] = i;
- area_accum += area;
- }
-
- if (!triangle_area_map.size() || area_accum == 0) {
-
- err_dialog->set_text(TTR("Faces contain no area!"));
- err_dialog->popup_centered_minsize();
- return;
- }
-
- int emissor_count = emission_amount->get_value();
-
- for (int i = 0; i < emissor_count; i++) {
-
- float areapos = Math::random(0.0f, area_accum);
-
- Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
- ERR_FAIL_COND(!E)
- int index = E->get();
- ERR_FAIL_INDEX(index, geometry.size());
-
- // ok FINALLY get face
- Face3 face = geometry[index];
- //now compute some position inside the face...
+ PoolVector<Vector3> points;
+ PoolVector<Vector3> normals;
- Vector3 pos = face.get_random_point_inside();
-
- points.push_back(pos.x);
- points.push_back(pos.y);
- points.push_back(pos.z);
-
- if (use_normals) {
- Vector3 normal = face.get_plane().normal;
- normals.push_back(normal.x);
- normals.push_back(normal.y);
- normals.push_back(normal.z);
- }
- }
- } else {
-
- int gcount = geometry.size();
-
- if (gcount == 0) {
-
- err_dialog->set_text(TTR("No faces!"));
- err_dialog->popup_centered_minsize();
- return;
- }
-
- PoolVector<Face3>::Read r = geometry.read();
-
- AABB aabb;
-
- for (int i = 0; i < gcount; i++) {
-
- for (int j = 0; j < 3; j++) {
-
- if (i == 0 && j == 0)
- aabb.position = r[i].vertex[j];
- else
- aabb.expand_to(r[i].vertex[j]);
- }
- }
-
- int emissor_count = emission_amount->get_value();
-
- for (int i = 0; i < emissor_count; i++) {
-
- int attempts = 5;
-
- for (int j = 0; j < attempts; j++) {
-
- Vector3 dir;
- dir[Math::rand() % 3] = 1.0;
- Vector3 ofs = (Vector3(1, 1, 1) - dir) * Vector3(Math::randf(), Math::randf(), Math::randf()) * aabb.size + aabb.position;
-
- Vector3 ofsv = ofs + aabb.size * dir;
-
- //space it a little
- ofs -= dir;
- ofsv += dir;
-
- float max = -1e7, min = 1e7;
-
- for (int k = 0; k < gcount; k++) {
-
- const Face3 &f3 = r[k];
-
- Vector3 res;
- if (f3.intersects_segment(ofs, ofsv, &res)) {
-
- res -= ofs;
- float d = dir.dot(res);
-
- if (d < min)
- min = d;
- if (d > max)
- max = d;
- }
- }
-
- if (max < min)
- continue; //lost attempt
-
- float val = min + (max - min) * Math::randf();
-
- Vector3 point = ofs + dir * val;
-
- points.push_back(point.x);
- points.push_back(point.y);
- points.push_back(point.z);
- break;
- }
- }
+ if (!_generate(points, normals)) {
+ return;
}
- int point_count = points.size() / 3;
+ int point_count = points.size();
int w = 2048;
int h = (point_count / 2048) + 1;
@@ -295,8 +369,13 @@ void ParticlesEditor::_generate_emission_points() {
{
PoolVector<uint8_t>::Write iw = point_img.write();
zeromem(iw.ptr(), w * h * 3 * sizeof(float));
- PoolVector<float>::Read r = points.read();
- copymem(iw.ptr(), r.ptr(), point_count * sizeof(float) * 3);
+ PoolVector<Vector3>::Read r = points.read();
+ float *wf = (float *)iw.ptr();
+ for (int i = 0; i < point_count; i++) {
+ wf[i * 3 + 0] = r[i].x;
+ wf[i * 3 + 1] = r[i].y;
+ wf[i * 3 + 2] = r[i].z;
+ }
}
Ref<Image> image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img));
@@ -308,7 +387,7 @@ void ParticlesEditor::_generate_emission_points() {
Ref<ParticlesMaterial> material = node->get_process_material();
ERR_FAIL_COND(material.is_null());
- if (use_normals) {
+ if (normals.size() > 0) {
material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
material->set_emission_point_count(point_count);
@@ -320,8 +399,13 @@ void ParticlesEditor::_generate_emission_points() {
{
PoolVector<uint8_t>::Write iw = point_img2.write();
zeromem(iw.ptr(), w * h * 3 * sizeof(float));
- PoolVector<float>::Read r = normals.read();
- copymem(iw.ptr(), r.ptr(), point_count * sizeof(float) * 3);
+ PoolVector<Vector3>::Read r = normals.read();
+ float *wf = (float *)iw.ptr();
+ for (int i = 0; i < point_count; i++) {
+ wf[i * 3 + 0] = r[i].x;
+ wf[i * 3 + 1] = r[i].y;
+ wf[i * 3 + 2] = r[i].z;
+ }
}
Ref<Image> image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2));
@@ -342,8 +426,6 @@ void ParticlesEditor::_generate_emission_points() {
void ParticlesEditor::_bind_methods() {
ClassDB::bind_method("_menu_option", &ParticlesEditor::_menu_option);
- ClassDB::bind_method("_node_selected", &ParticlesEditor::_node_selected);
- ClassDB::bind_method("_generate_emission_points", &ParticlesEditor::_generate_emission_points);
ClassDB::bind_method("_generate_aabb", &ParticlesEditor::_generate_aabb);
}
@@ -360,49 +442,10 @@ ParticlesEditor::ParticlesEditor() {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Emission Points From Mesh"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
- options->get_popup()->connect("id_pressed", this, "_menu_option");
-
- emission_dialog = memnew(ConfirmationDialog);
- emission_dialog->set_title(TTR("Create Emitter"));
- add_child(emission_dialog);
- VBoxContainer *emd_vb = memnew(VBoxContainer);
- emission_dialog->add_child(emd_vb);
-
- emission_amount = memnew(SpinBox);
- emission_amount->set_min(1);
- emission_amount->set_max(100000);
- emission_amount->set_value(512);
- emd_vb->add_margin_child(TTR("Emission Points:"), emission_amount);
-
- emission_fill = memnew(OptionButton);
- emission_fill->add_item(TTR("Surface Points"));
- emission_fill->add_item(TTR("Surface Points+Normal (Directed)"));
- emission_fill->add_item(TTR("Volume"));
- emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill);
-
- emission_dialog->get_ok()->set_text(TTR("Create"));
- emission_dialog->connect("confirmed", this, "_generate_emission_points");
-
- err_dialog = memnew(ConfirmationDialog);
- add_child(err_dialog);
-
- emission_file_dialog = memnew(EditorFileDialog);
- add_child(emission_file_dialog);
- emission_file_dialog->connect("file_selected", this, "_resource_seleted");
- emission_tree_dialog = memnew(SceneTreeDialog);
- add_child(emission_tree_dialog);
- emission_tree_dialog->connect("selected", this, "_node_selected");
-
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type("Mesh", &extensions);
-
- emission_file_dialog->clear_filters();
- for (int i = 0; i < extensions.size(); i++) {
-
- emission_file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
- }
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES);
- emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ options->get_popup()->connect("id_pressed", this, "_menu_option");
generate_aabb = memnew(ConfirmationDialog);
generate_aabb->set_title(TTR("Generate Visibility AABB"));
diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h
index 013b6e7e30..622ce6e8a9 100644
--- a/editor/plugins/particles_editor_plugin.h
+++ b/editor/plugins/particles_editor_plugin.h
@@ -40,14 +40,14 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
-class ParticlesEditor : public Control {
-
- GDCLASS(ParticlesEditor, Control);
+class ParticlesEditorBase : public Control {
+ GDCLASS(ParticlesEditorBase, Control)
+protected:
+ Spatial *base_node;
Panel *panel;
MenuButton *options;
HBoxContainer *particles_editor_hb;
- Particles *node;
EditorFileDialog *emission_file_dialog;
SceneTreeDialog *emission_tree_dialog;
@@ -58,8 +58,25 @@ class ParticlesEditor : public Control {
SpinBox *emission_amount;
OptionButton *emission_fill;
+ PoolVector<Face3> geometry;
+
+ bool _generate(PoolVector<Vector3> &points, PoolVector<Vector3> &normals);
+ virtual void _generate_emission_points() = 0;
+ void _node_selected(const NodePath &p_path);
+
+ static void _bind_methods();
+
+public:
+ ParticlesEditorBase();
+};
+
+class ParticlesEditor : public ParticlesEditorBase {
+
+ GDCLASS(ParticlesEditor, ParticlesEditorBase);
+
ConfirmationDialog *generate_aabb;
SpinBox *generate_seconds;
+ Particles *node;
enum Menu {
@@ -67,21 +84,18 @@ class ParticlesEditor : public Control {
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH,
MENU_OPTION_CLEAR_EMISSION_VOLUME,
+ MENU_OPTION_CONVERT_TO_CPU_PARTICLES,
};
- PoolVector<Face3> geometry;
-
void _generate_aabb();
- void _generate_emission_points();
- void _node_selected(const NodePath &p_path);
void _menu_option(int);
- void _populate();
-
friend class ParticlesEditorPlugin;
+ virtual void _generate_emission_points();
+
protected:
void _notification(int p_notification);
void _node_removed(Node *p_node);
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 5ec42b07aa..33e182faef 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -109,6 +109,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
action_point = i;
moving_from = curve->get_point_out(i);
moving_screen_from = gpoint;
+ orig_in_length = curve->get_point_in(action_point).length();
return true;
} else if (dist_to_p_in < grab_threshold && i > 0) {
@@ -116,6 +117,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
action_point = i;
moving_from = curve->get_point_in(i);
moving_screen_from = gpoint;
+ orig_out_length = curve->get_point_out(action_point).length();
return true;
}
}
@@ -205,6 +207,11 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move In-Control in Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, new_pos);
undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, moving_from);
+
+ if (mirror_handle_angle) {
+ undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
+ undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
+ }
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
@@ -216,6 +223,11 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move Out-Control in Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, new_pos);
undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, moving_from);
+
+ if (mirror_handle_angle) {
+ undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
+ undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
+ }
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
@@ -255,10 +267,16 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
case ACTION_MOVING_IN: {
curve->set_point_in(action_point, new_pos);
+
+ if (mirror_handle_angle)
+ curve->set_point_out(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
} break;
case ACTION_MOVING_OUT: {
curve->set_point_out(action_point, new_pos);
+
+ if (mirror_handle_angle)
+ curve->set_point_in(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
} break;
}
@@ -342,6 +360,7 @@ void Path2DEditor::_bind_methods() {
//ClassDB::bind_method(D_METHOD("_menu_option"),&Path2DEditor::_menu_option);
ClassDB::bind_method(D_METHOD("_node_visibility_changed"), &Path2DEditor::_node_visibility_changed);
ClassDB::bind_method(D_METHOD("_mode_selected"), &Path2DEditor::_mode_selected);
+ ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &Path2DEditor::_handle_option_pressed);
}
void Path2DEditor::_mode_selected(int p_mode) {
@@ -396,11 +415,33 @@ void Path2DEditor::_mode_selected(int p_mode) {
mode = Mode(p_mode);
}
+void Path2DEditor::_handle_option_pressed(int p_option) {
+
+ PopupMenu *pm;
+ pm = handle_menu->get_popup();
+
+ switch (p_option) {
+ case HANDLE_OPTION_ANGLE: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
+ mirror_handle_angle = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
+ } break;
+ case HANDLE_OPTION_LENGTH: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
+ mirror_handle_length = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ } break;
+ }
+}
+
Path2DEditor::Path2DEditor(EditorNode *p_editor) {
canvas_item_editor = NULL;
editor = p_editor;
undo_redo = editor->get_undo_redo();
+ mirror_handle_angle = true;
+ mirror_handle_length = true;
mode = MODE_EDIT;
action = ACTION_NONE;
@@ -444,6 +485,20 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) {
curve_close->set_tooltip(TTR("Close Curve"));
curve_close->connect("pressed", this, "_mode_selected", varray(ACTION_CLOSE));
base_hb->add_child(curve_close);
+
+ PopupMenu *menu;
+
+ handle_menu = memnew(MenuButton);
+ handle_menu->set_text(TTR("Options"));
+ base_hb->add_child(handle_menu);
+
+ menu = handle_menu->get_popup();
+ menu->add_check_item(TTR("Mirror Handle Angles"));
+ menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ menu->add_check_item(TTR("Mirror Handle Lengths"));
+ menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ menu->connect("id_pressed", this, "_handle_option_pressed");
+
base_hb->hide();
curve_edit->set_pressed(true);
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
index c92a696967..1e3955f84f 100644
--- a/editor/plugins/path_2d_editor_plugin.h
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -69,6 +69,15 @@ class Path2DEditor : public HBoxContainer {
ToolButton *curve_edit_curve;
ToolButton *curve_del;
ToolButton *curve_close;
+ MenuButton *handle_menu;
+
+ bool mirror_handle_angle;
+ bool mirror_handle_length;
+
+ enum HandleOption {
+ HANDLE_OPTION_ANGLE,
+ HANDLE_OPTION_LENGTH
+ };
enum Action {
@@ -82,8 +91,11 @@ class Path2DEditor : public HBoxContainer {
int action_point;
Point2 moving_from;
Point2 moving_screen_from;
+ float orig_in_length;
+ float orig_out_length;
void _mode_selected(int p_mode);
+ void _handle_option_pressed(int p_option);
void _node_visibility_changed();
friend class Path2DEditorPlugin;
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 6dde639c54..72a8b55a52 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -128,11 +128,22 @@ void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_p
if (p.intersects_ray(ray_from, ray_dir, &inters)) {
+ if (!PathEditorPlugin::singleton->is_handle_clicked()) {
+ orig_in_length = c->get_point_in(idx).length();
+ orig_out_length = c->get_point_out(idx).length();
+ PathEditorPlugin::singleton->set_handle_clicked(true);
+ }
+
Vector3 local = gi.xform(inters) - base;
if (t == 0) {
c->set_point_in(idx, local);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled())
+ c->set_point_out(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length));
} else {
c->set_point_out(idx, local);
+ if (PathEditorPlugin::singleton->mirror_angle_enabled())
+ c->set_point_in(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_in_length));
}
}
}
@@ -165,8 +176,6 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
int idx = p_idx / 2;
int t = p_idx % 2;
- Vector3 ofs;
-
if (t == 0) {
if (p_cancel) {
c->set_point_in(p_idx, p_restore);
@@ -176,6 +185,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
ur->create_action(TTR("Set Curve In Position"));
ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
+ ur->add_do_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length));
+ ur->add_undo_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_out_length));
+ }
ur->commit_action();
} else {
@@ -188,6 +202,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
ur->create_action(TTR("Set Curve Out Position"));
ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
+ ur->add_do_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length));
+ ur->add_undo_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_in_length));
+ }
ur->commit_action();
}
}
@@ -291,6 +310,9 @@ bool PathEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp
Point2 mbpos(mb->get_position().x, mb->get_position().y);
+ if (!mb->is_pressed())
+ set_handle_clicked(false);
+
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->get_control()))) {
//click into curve, break it down
PoolVector<Vector3> v3a = c->tessellate();
@@ -459,6 +481,7 @@ void PathEditorPlugin::make_visible(bool p_visible) {
curve_edit->show();
curve_del->show();
curve_close->show();
+ handle_menu->show();
sep->show();
} else {
@@ -466,6 +489,7 @@ void PathEditorPlugin::make_visible(bool p_visible) {
curve_edit->hide();
curve_del->hide();
curve_close->hide();
+ handle_menu->hide();
sep->hide();
{
@@ -495,6 +519,26 @@ void PathEditorPlugin::_close_curve() {
c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0));
}
+void PathEditorPlugin::_handle_option_pressed(int p_option) {
+
+ PopupMenu *pm;
+ pm = handle_menu->get_popup();
+
+ switch (p_option) {
+ case HANDLE_OPTION_ANGLE: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
+ mirror_handle_angle = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
+ } break;
+ case HANDLE_OPTION_LENGTH: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
+ mirror_handle_length = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ } break;
+ }
+}
+
void PathEditorPlugin::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -510,6 +554,7 @@ void PathEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_mode_changed"), &PathEditorPlugin::_mode_changed);
ClassDB::bind_method(D_METHOD("_close_curve"), &PathEditorPlugin::_close_curve);
+ ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &PathEditorPlugin::_handle_option_pressed);
}
PathEditorPlugin *PathEditorPlugin::singleton = NULL;
@@ -519,6 +564,8 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
path = NULL;
editor = p_node;
singleton = this;
+ mirror_handle_angle = true;
+ mirror_handle_length = true;
path_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
path_material->set_albedo(Color(0.5, 0.5, 1.0, 0.8));
@@ -567,6 +614,20 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
curve_close->set_tooltip(TTR("Close Curve"));
SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
+ PopupMenu *menu;
+
+ handle_menu = memnew(MenuButton);
+ handle_menu->set_text(TTR("Options"));
+ handle_menu->hide();
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(handle_menu);
+
+ menu = handle_menu->get_popup();
+ menu->add_check_item(TTR("Mirror Handle Angles"));
+ menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ menu->add_check_item(TTR("Mirror Handle Lengths"));
+ menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ menu->connect("id_pressed", this, "_handle_option_pressed");
+
curve_edit->set_pressed(true);
/*
collision_polygon_editor = memnew( PathEditor(p_node) );
diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h
index 6d5f07f729..52dfb78b61 100644
--- a/editor/plugins/path_editor_plugin.h
+++ b/editor/plugins/path_editor_plugin.h
@@ -1,4 +1,4 @@
-/*************************************************************************/
+/*************************************************************************/
/* path_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
@@ -40,6 +40,8 @@ class PathSpatialGizmo : public EditorSpatialGizmo {
Path *path;
mutable Vector3 original;
+ mutable float orig_in_length;
+ mutable float orig_out_length;
public:
virtual String get_handle_name(int p_idx) const;
@@ -60,6 +62,7 @@ class PathEditorPlugin : public EditorPlugin {
ToolButton *curve_edit;
ToolButton *curve_del;
ToolButton *curve_close;
+ MenuButton *handle_menu;
EditorNode *editor;
@@ -67,6 +70,15 @@ class PathEditorPlugin : public EditorPlugin {
void _mode_changed(int p_idx);
void _close_curve();
+ void _handle_option_pressed(int p_option);
+ bool handle_clicked;
+ bool mirror_handle_angle;
+ bool mirror_handle_length;
+
+ enum HandleOption {
+ HANDLE_OPTION_ANGLE,
+ HANDLE_OPTION_LENGTH
+ };
protected:
void _notification(int p_what);
@@ -88,6 +100,11 @@ public:
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
+ bool mirror_angle_enabled() { return mirror_handle_angle; }
+ bool mirror_length_enabled() { return mirror_handle_length; }
+ bool is_handle_clicked() { return handle_clicked; }
+ void set_handle_clicked(bool clicked) { handle_clicked = clicked; }
+
PathEditorPlugin(EditorNode *p_node);
~PathEditorPlugin();
};
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index ed41e1931e..4840b1899d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -563,6 +563,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_REMOVE_SPLIT) {
+ splits_prev = node->get_splits();
for (int i = 0; i < splits_prev.size(); i += 2) {
if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size())
continue;
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
new file mode 100644
index 0000000000..89c1b3a978
--- /dev/null
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -0,0 +1,293 @@
+#include "root_motion_editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/main/viewport.h"
+
+void EditorPropertyRootMotion::_confirmed() {
+
+ TreeItem *ti = filters->get_selected();
+ if (!ti)
+ return;
+
+ NodePath path = ti->get_metadata(0);
+ emit_signal("property_changed", get_edited_property(), path);
+ update_property();
+ filter_dialog->hide(); //may come from activated
+}
+
+void EditorPropertyRootMotion::_node_assign() {
+
+ NodePath current = get_edited_object()->get(get_edited_property());
+
+ AnimationTree *atree = Object::cast_to<AnimationTree>(get_edited_object());
+ if (!atree->has_node(atree->get_animation_player())) {
+ EditorNode::get_singleton()->show_warning(TTR("AnimationTree has no path set to an AnimationPlayer"));
+ return;
+ }
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(atree->get_node(atree->get_animation_player()));
+ if (!player) {
+ EditorNode::get_singleton()->show_warning(TTR("Path to AnimationPlayer is invalid"));
+ return;
+ }
+
+ Node *base = player->get_node(player->get_root());
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names."));
+ return;
+ }
+
+ Set<String> paths;
+ {
+ List<StringName> animations;
+ player->get_animation_list(&animations);
+
+ for (List<StringName>::Element *E = animations.front(); E; E = E->next()) {
+
+ Ref<Animation> anim = player->get_animation(E->get());
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ paths.insert(anim->track_get_path(i));
+ }
+ }
+ }
+
+ filters->clear();
+ TreeItem *root = filters->create_item();
+
+ Map<String, TreeItem *> parenthood;
+
+ for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
+
+ NodePath path = E->get();
+ TreeItem *ti = NULL;
+ String accum;
+ for (int i = 0; i < path.get_name_count(); i++) {
+ String name = path.get_name(i);
+ if (accum != String()) {
+ accum += "/";
+ }
+ accum += name;
+ if (!parenthood.has(accum)) {
+ if (ti) {
+ ti = filters->create_item(ti);
+ } else {
+ ti = filters->create_item(root);
+ }
+ parenthood[accum] = ti;
+ ti->set_text(0, name);
+ ti->set_selectable(0, false);
+ ti->set_editable(0, false);
+
+ if (base->has_node(accum)) {
+ Node *node = base->get_node(accum);
+ if (has_icon(node->get_class(), "EditorIcons")) {
+ ti->set_icon(0, get_icon(node->get_class(), "EditorIcons"));
+ } else {
+ ti->set_icon(0, get_icon("Node", "EditorIcons"));
+ }
+ }
+
+ } else {
+ ti = parenthood[accum];
+ }
+ }
+
+ Node *node = NULL;
+ if (base->has_node(accum)) {
+ node = base->get_node(accum);
+ }
+ if (!node)
+ continue; //no node, cant edit
+
+ if (path.get_subname_count()) {
+
+ String concat = path.get_concatenated_subnames();
+
+ Skeleton *skeleton = Object::cast_to<Skeleton>(node);
+ if (skeleton && skeleton->find_bone(concat) != -1) {
+ //path in skeleton
+ String bone = concat;
+ int idx = skeleton->find_bone(bone);
+ List<String> bone_path;
+ while (idx != -1) {
+ bone_path.push_front(skeleton->get_bone_name(idx));
+ idx = skeleton->get_bone_parent(idx);
+ }
+
+ accum += ":";
+ for (List<String>::Element *F = bone_path.front(); F; F = F->next()) {
+ if (F != bone_path.front()) {
+ accum += "/";
+ }
+
+ accum += F->get();
+ if (!parenthood.has(accum)) {
+ ti = filters->create_item(ti);
+ parenthood[accum] = ti;
+ ti->set_text(0, F->get());
+ ti->set_selectable(0, true);
+ ti->set_editable(0, false);
+ ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
+ ti->set_metadata(0, accum);
+ } else {
+ ti = parenthood[accum];
+ }
+ }
+
+ ti->set_selectable(0, true);
+ ti->set_text(0, concat);
+ ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
+ ti->set_metadata(0, path);
+ if (path == current) {
+ ti->select(0);
+ }
+
+ } else {
+ //just a property
+ ti = filters->create_item(ti);
+ ti->set_text(0, concat);
+ ti->set_selectable(0, true);
+ ti->set_metadata(0, path);
+ if (path == current) {
+ ti->select(0);
+ }
+ }
+ } else {
+ if (ti) {
+ //just a node, likely call or animation track
+ ti->set_selectable(0, true);
+ ti->set_metadata(0, path);
+ if (path == current) {
+ ti->select(0);
+ }
+ }
+ }
+ }
+
+ filters->ensure_cursor_is_visible();
+ filter_dialog->popup_centered_ratio();
+}
+
+void EditorPropertyRootMotion::_node_clear() {
+
+ emit_signal("property_changed", get_edited_property(), NodePath());
+ update_property();
+}
+
+void EditorPropertyRootMotion::update_property() {
+
+ NodePath p = get_edited_object()->get(get_edited_property());
+
+ assign->set_tooltip(p);
+ if (p == NodePath()) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(TTR("Assign.."));
+ assign->set_flat(false);
+ return;
+ }
+ assign->set_flat(true);
+
+ Node *base_node = NULL;
+ if (base_hint != NodePath()) {
+ if (get_tree()->get_root()->has_node(base_hint)) {
+ base_node = get_tree()->get_root()->get_node(base_hint);
+ }
+ } else {
+ base_node = Object::cast_to<Node>(get_edited_object());
+ }
+
+ if (!base_node || !base_node->has_node(p)) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(p);
+ return;
+ }
+
+ Node *target_node = base_node->get_node(p);
+ ERR_FAIL_COND(!target_node);
+
+ assign->set_text(target_node->get_name());
+
+ Ref<Texture> icon;
+ if (has_icon(target_node->get_class(), "EditorIcons"))
+ icon = get_icon(target_node->get_class(), "EditorIcons");
+ else
+ icon = get_icon("Node", "EditorIcons");
+
+ assign->set_icon(icon);
+}
+
+void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) {
+
+ base_hint = p_base_hint;
+}
+
+void EditorPropertyRootMotion::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Ref<Texture> t = get_icon("Clear", "EditorIcons");
+ clear->set_icon(t);
+ }
+}
+
+void EditorPropertyRootMotion::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_confirmed"), &EditorPropertyRootMotion::_confirmed);
+ ClassDB::bind_method(D_METHOD("_node_assign"), &EditorPropertyRootMotion::_node_assign);
+ ClassDB::bind_method(D_METHOD("_node_clear"), &EditorPropertyRootMotion::_node_clear);
+}
+
+EditorPropertyRootMotion::EditorPropertyRootMotion() {
+
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ add_child(hbc);
+ assign = memnew(Button);
+ assign->set_flat(true);
+ assign->set_h_size_flags(SIZE_EXPAND_FILL);
+ assign->set_clip_text(true);
+ assign->connect("pressed", this, "_node_assign");
+ hbc->add_child(assign);
+
+ clear = memnew(Button);
+ clear->set_flat(true);
+ clear->connect("pressed", this, "_node_clear");
+ hbc->add_child(clear);
+
+ filter_dialog = memnew(ConfirmationDialog);
+ add_child(filter_dialog);
+ filter_dialog->set_title(TTR("Edit Filtered Tracks:"));
+ filter_dialog->connect("confirmed", this, "_confirmed");
+
+ filters = memnew(Tree);
+ filter_dialog->add_child(filters);
+ filters->set_v_size_flags(SIZE_EXPAND_FILL);
+ filters->set_hide_root(true);
+ filters->connect("item_activated", this, "_confirmed");
+ //filters->connect("item_edited", this, "_filter_edited");
+}
+//////////////////////////
+
+bool EditorInspectorRootMotionPlugin::can_handle(Object *p_object) {
+ return true; //can handle everything
+}
+
+void EditorInspectorRootMotionPlugin::parse_begin(Object *p_object) {
+ //do none
+}
+
+bool EditorInspectorRootMotionPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ if (p_path == "root_motion_track" && p_object->is_class("AnimationTree") && p_type == Variant::NODE_PATH) {
+ print_line("use custom!");
+ EditorPropertyRootMotion *editor = memnew(EditorPropertyRootMotion);
+ if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) {
+ editor->setup(p_hint_text);
+ }
+ add_property_editor(p_path, editor);
+ return true;
+ }
+
+ return false; //can be overriden, although it will most likely be last anyway
+}
+
+void EditorInspectorRootMotionPlugin::parse_end() {
+ //do none
+}
diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h
new file mode 100644
index 0000000000..84af47872f
--- /dev/null
+++ b/editor/plugins/root_motion_editor_plugin.h
@@ -0,0 +1,42 @@
+#ifndef ROOT_MOTION_EDITOR_PLUGIN_H
+#define ROOT_MOTION_EDITOR_PLUGIN_H
+
+#include "editor/editor_inspector.h"
+#include "editor/editor_spin_slider.h"
+#include "editor/property_selector.h"
+#include "scene/animation/animation_tree.h"
+
+class EditorPropertyRootMotion : public EditorProperty {
+ GDCLASS(EditorPropertyRootMotion, EditorProperty)
+ Button *assign;
+ Button *clear;
+ NodePath base_hint;
+
+ ConfirmationDialog *filter_dialog;
+ Tree *filters;
+
+ void _confirmed();
+ void _node_assign();
+ void _node_clear();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual void update_property();
+ void setup(const NodePath &p_base_hint);
+ EditorPropertyRootMotion();
+};
+
+class EditorInspectorRootMotionPlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorRootMotionPlugin, EditorInspectorPlugin)
+
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+ virtual void parse_end();
+};
+
+#endif // ROOT_MOTION_EDITOR_PLUGIN_H
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 83072534b8..876da7f61a 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -804,12 +804,12 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) {
void ScriptEditor::_file_dialog_action(String p_file) {
switch (file_dialog_option) {
- case FILE_SAVE_THEME_AS: {
+ case THEME_SAVE_AS: {
if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
}
} break;
- case FILE_IMPORT_THEME: {
+ case THEME_IMPORT: {
if (!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) {
editor->show_warning(TTR("Error importing theme"), TTR("Error importing"));
}
@@ -859,33 +859,6 @@ void ScriptEditor::_menu_option(int p_option) {
save_all_scripts();
} break;
- case FILE_IMPORT_THEME: {
- file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
- file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
- file_dialog_option = FILE_IMPORT_THEME;
- file_dialog->clear_filters();
- file_dialog->add_filter("*.tet");
- file_dialog->popup_centered_ratio();
- file_dialog->set_title(TTR("Import Theme"));
- } break;
- case FILE_RELOAD_THEME: {
- EditorSettings::get_singleton()->load_text_editor_theme();
- } break;
- case FILE_SAVE_THEME: {
- if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
- editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
- }
- } break;
- case FILE_SAVE_THEME_AS: {
- file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
- file_dialog_option = FILE_SAVE_THEME_AS;
- file_dialog->clear_filters();
- file_dialog->add_filter("*.tet");
- file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
- file_dialog->popup_centered_ratio();
- file_dialog->set_title(TTR("Save Theme As..."));
- } break;
case SEARCH_HELP: {
help_search_dialog->popup();
@@ -1143,6 +1116,38 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
+void ScriptEditor::_theme_option(int p_option) {
+ switch (p_option) {
+ case THEME_IMPORT: {
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_dialog_option = THEME_IMPORT;
+ file_dialog->clear_filters();
+ file_dialog->add_filter("*.tet");
+ file_dialog->popup_centered_ratio();
+ file_dialog->set_title(TTR("Import Theme"));
+ } break;
+ case THEME_RELOAD: {
+ EditorSettings::get_singleton()->load_text_editor_theme();
+ } break;
+ case THEME_SAVE: {
+ if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
+ editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
+ }
+ } break;
+ case THEME_SAVE_AS: {
+ file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_dialog_option = THEME_SAVE_AS;
+ file_dialog->clear_filters();
+ file_dialog->add_filter("*.tet");
+ file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
+ file_dialog->popup_centered_ratio();
+ file_dialog->set_title(TTR("Save Theme As..."));
+ } break;
+ }
+}
+
void ScriptEditor::_tab_changed(int p_which) {
ensure_select_current();
@@ -1867,6 +1872,7 @@ void ScriptEditor::save_all_scripts() {
}
_update_script_names();
+ EditorFileSystem::get_singleton()->update_script_classes();
}
void ScriptEditor::apply_scripts() const {
@@ -2591,6 +2597,7 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_close_all_tabs", &ScriptEditor::_close_all_tabs);
ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
+ ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option);
ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop);
@@ -2763,10 +2770,18 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme")), FILE_IMPORT_THEME);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), FILE_RELOAD_THEME);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), FILE_SAVE_THEME);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As")), FILE_SAVE_THEME_AS);
+
+ file_menu->get_popup()->add_submenu_item(TTR("Theme"), "Theme", FILE_THEME);
+
+ theme_submenu = memnew(PopupMenu);
+ theme_submenu->set_name("Theme");
+ file_menu->get_popup()->add_child(theme_submenu);
+ theme_submenu->connect("id_pressed", this, "_theme_option");
+ theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme")), THEME_IMPORT);
+ theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), THEME_RELOAD);
+ theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), THEME_SAVE);
+ theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As")), THEME_SAVE_AS);
+
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
@@ -2870,7 +2885,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
error_dialog = memnew(AcceptDialog);
add_child(error_dialog);
- error_dialog->get_ok()->set_text(TTR("I see.."));
+ error_dialog->get_ok()->set_text(TTR("I see..."));
debugger = memnew(ScriptEditorDebugger(editor));
debugger->connect("goto_script_line", this, "_goto_script_line");
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 769612bdb6..ad12add53f 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -72,9 +72,9 @@ public:
class ScriptEditorDebugger;
-class ScriptEditorBase : public Control {
+class ScriptEditorBase : public VBoxContainer {
- GDCLASS(ScriptEditorBase, Control);
+ GDCLASS(ScriptEditorBase, VBoxContainer);
protected:
static void _bind_methods();
@@ -134,10 +134,7 @@ class ScriptEditor : public PanelContainer {
FILE_SAVE,
FILE_SAVE_AS,
FILE_SAVE_ALL,
- FILE_IMPORT_THEME,
- FILE_RELOAD_THEME,
- FILE_SAVE_THEME,
- FILE_SAVE_THEME_AS,
+ FILE_THEME,
FILE_RUN,
FILE_CLOSE,
CLOSE_DOCS,
@@ -168,6 +165,13 @@ class ScriptEditor : public PanelContainer {
WINDOW_SELECT_BASE = 100
};
+ enum {
+ THEME_IMPORT,
+ THEME_RELOAD,
+ THEME_SAVE,
+ THEME_SAVE_AS
+ };
+
enum ScriptSortBy {
SORT_BY_NAME,
SORT_BY_PATH,
@@ -190,6 +194,7 @@ class ScriptEditor : public PanelContainer {
uint64_t idle;
PopupMenu *recent_scripts;
+ PopupMenu *theme_submenu;
Button *help_search;
Button *site_search;
@@ -251,6 +256,7 @@ class ScriptEditor : public PanelContainer {
void _tab_changed(int p_which);
void _menu_option(int p_option);
+ void _theme_option(int p_option);
Tree *disk_changed_list;
ConfirmationDialog *disk_changed;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index aef2a53dd1..2263d782d9 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -116,6 +116,7 @@ void ScriptTextEditor::_load_theme_settings() {
Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color");
+ Color safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color");
Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color");
Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color");
Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color");
@@ -147,6 +148,7 @@ void ScriptTextEditor::_load_theme_settings() {
text_edit->add_color_override("completion_font_color", completion_font_color);
text_edit->add_color_override("font_color", text_color);
text_edit->add_color_override("line_number_color", line_number_color);
+ text_edit->add_color_override("safe_line_number_color", safe_line_number_color);
text_edit->add_color_override("caret_color", caret_color);
text_edit->add_color_override("caret_background_color", caret_background_color);
text_edit->add_color_override("font_selected_color", text_selected_color);
@@ -589,6 +591,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
emit_signal("name_changed");
code_editor->update_line_and_column();
+ call_deferred("_validate_script");
}
void ScriptTextEditor::_validate_script() {
@@ -599,8 +602,9 @@ void ScriptTextEditor::_validate_script() {
String text = te->get_text();
List<String> fnc;
+ Set<int> safe_lines;
- if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc)) {
+ if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc, &safe_lines)) {
String error_text = "error(" + itos(line) + "," + itos(col) + "): " + errortxt;
code_editor->set_error(error_text);
} else {
@@ -621,8 +625,23 @@ void ScriptTextEditor::_validate_script() {
}
line--;
+ bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true);
+ bool last_is_safe = false;
for (int i = 0; i < te->get_line_count(); i++) {
te->set_line_as_marked(i, line == i);
+ if (highlight_safe) {
+ if (safe_lines.has(i + 1)) {
+ te->set_line_as_safe(i, true);
+ last_is_safe = true;
+ } else if (last_is_safe && (te->is_line_comment(i) || te->get_line(i).strip_edges().empty())) {
+ te->set_line_as_safe(i, true);
+ } else {
+ te->set_line_as_safe(i, false);
+ last_is_safe = false;
+ }
+ } else {
+ te->set_line_as_safe(i, false);
+ }
}
emit_signal("name_changed");
@@ -1289,16 +1308,26 @@ void ScriptTextEditor::_edit_option(int p_op) {
void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
highlighters[p_highlighter->get_name()] = p_highlighter;
- highlighter_menu->get_popup()->add_item(p_highlighter->get_name());
+ highlighter_menu->add_radio_check_item(p_highlighter->get_name());
}
void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
TextEdit *te = code_editor->get_text_edit();
te->_set_syntax_highlighting(p_highlighter);
+ if (p_highlighter != NULL)
+ highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->get_name()), true);
+ else
+ highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text("Standard"), true);
}
void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
- set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]);
+ Map<String, SyntaxHighlighter *>::Element *el = highlighters.front();
+ while (el != NULL) {
+ highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false);
+ el = el->next();
+ }
+ // highlighter_menu->set_item_checked(p_idx, true);
+ set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]);
}
void ScriptTextEditor::_bind_methods() {
@@ -1609,6 +1638,7 @@ ScriptTextEditor::ScriptTextEditor() {
code_editor->set_code_complete_func(_code_complete_scripts, this);
code_editor->get_text_edit()->connect("breakpoint_toggled", this, "_breakpoint_toggled");
code_editor->get_text_edit()->connect("symbol_lookup", this, "_lookup_symbol");
+ code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
update_settings();
@@ -1666,6 +1696,7 @@ ScriptTextEditor::ScriptTextEditor() {
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_breakpoint"), DEBUG_GOTO_NEXT_BREAKPOINT);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_breakpoint"), DEBUG_GOTO_PREV_BREAKPOINT);
edit_menu->get_popup()->add_separator();
+
PopupMenu *convert_case = memnew(PopupMenu);
convert_case->set_name("convert_case");
edit_menu->get_popup()->add_child(convert_case);
@@ -1675,6 +1706,14 @@ ScriptTextEditor::ScriptTextEditor() {
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE);
convert_case->connect("id_pressed", this, "_edit_option");
+ highlighters["Standard"] = NULL;
+ highlighter_menu = memnew(PopupMenu);
+ highlighter_menu->set_name("highlighter_menu");
+ edit_menu->get_popup()->add_child(highlighter_menu);
+ edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu");
+ highlighter_menu->add_radio_check_item(TTR("Standard"));
+ highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter");
+
search_menu = memnew(MenuButton);
edit_hb->add_child(search_menu);
search_menu->set_text(TTR("Search"));
@@ -1694,14 +1733,6 @@ ScriptTextEditor::ScriptTextEditor() {
edit_hb->add_child(edit_menu);
- highlighters["Standard"] = NULL;
-
- highlighter_menu = memnew(MenuButton);
- highlighter_menu->set_text(TTR("Syntax Highlighter"));
- highlighter_menu->get_popup()->add_item("Standard");
- highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter");
- edit_hb->add_child(highlighter_menu);
-
quick_open = memnew(ScriptEditorQuickOpen);
add_child(quick_open);
quick_open->connect("goto_line", this, "_goto_line");
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index a93e1a6fa8..a415f478e8 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -49,8 +49,8 @@ class ScriptTextEditor : public ScriptEditorBase {
HBoxContainer *edit_hb;
MenuButton *edit_menu;
- MenuButton *highlighter_menu;
MenuButton *search_menu;
+ PopupMenu *highlighter_menu;
PopupMenu *context_menu;
GotoLineDialog *goto_line_dialog;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 9b31e1a421..4b7f27c0c1 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -130,9 +130,9 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- for (const Set<String>::Element *E = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).size(); i++) {
- keywords.push_back(E->get());
+ keywords.push_back(ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode()))[i]);
}
}
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 30fff474d7..37b8562e96 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -471,7 +471,11 @@ void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_incl
Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {
CameraMatrix cm;
- cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ if (orthogonal) {
+ cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ } else {
+ cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ }
float screen_w, screen_h;
cm.get_viewport_size(screen_w, screen_h);
@@ -518,18 +522,24 @@ void SpatialEditorViewport::_select_region() {
Vector3 a = _get_screen_to_space(box[i]);
Vector3 b = _get_screen_to_space(box[(i + 1) % 4]);
- frustum.push_back(Plane(a, b, cam_pos));
+ if (orthogonal) {
+ frustum.push_back(Plane(a, (a - b).normalized()));
+ } else {
+ frustum.push_back(Plane(a, b, cam_pos));
+ }
}
- Plane near(cam_pos, -_get_camera_normal());
- near.d -= get_znear();
+ if (!orthogonal) {
+ Plane near(cam_pos, -_get_camera_normal());
+ near.d -= get_znear();
- frustum.push_back(near);
+ frustum.push_back(near);
- Plane far = -near;
- far.d += get_zfar();
+ Plane far = -near;
+ far.d += get_zfar();
- frustum.push_back(far);
+ frustum.push_back(far);
+ }
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario());
Vector<Spatial *> selected;
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 637926a913..af882f6e05 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -60,6 +60,7 @@ public:
virtual Variant get_handle_value(int p_idx) const;
virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ virtual bool is_gizmo_handle_highlighted(int idx) const { return false; }
virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index ea133cd749..19646f37b5 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -133,16 +133,14 @@ void TileMapEditor::_menu_option(int p_option) {
if (!selection_active)
return;
- undo_redo->create_action(TTR("Erase Selection"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Erase Selection"));
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ _set_cell(Point2i(j, i), invalid_cell, false, false, false);
}
}
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
selection_active = false;
copydata.clear();
@@ -188,30 +186,84 @@ void TileMapEditor::_canvas_mouse_exit() {
canvas_item_editor->update();
}
-int TileMapEditor::get_selected_tile() const {
+Vector<int> TileMapEditor::get_selected_tiles() const {
+
+ Vector<int> items = palette->get_selected_items();
+
+ if (items.size() == 0) {
+ items.push_back(TileMap::INVALID_CELL);
+ return items;
+ }
+
+ for (int i = items.size() - 1; i >= 0; i--) {
+ items[i] = palette->get_item_metadata(items[i]);
+ }
+ return items;
+}
+
+void TileMapEditor::set_selected_tiles(Vector<int> p_tiles) {
+
+ palette->unselect_all();
+
+ for (int i = p_tiles.size() - 1; i >= 0; i--) {
+ int idx = palette->find_metadata(p_tiles[i]);
+
+ if (idx >= 0) {
+ palette->select(idx, false);
+ }
+ }
+
+ palette->ensure_current_is_visible();
+}
+
+void TileMapEditor::_create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) {
+
+ Dictionary cell_old;
+ Dictionary cell_new;
+
+ cell_old["id"] = p_cell_old.idx;
+ cell_old["flip_h"] = p_cell_old.xf;
+ cell_old["flip_y"] = p_cell_old.yf;
+ cell_old["transpose"] = p_cell_old.tr;
+ cell_old["auto_coord"] = p_cell_old.ac;
+
+ cell_new["id"] = p_cell_new.idx;
+ cell_new["flip_h"] = p_cell_new.xf;
+ cell_new["flip_y"] = p_cell_new.yf;
+ cell_new["transpose"] = p_cell_new.tr;
+ cell_new["auto_coord"] = p_cell_new.ac;
- int item = palette->get_current();
+ undo_redo->add_undo_method(node, "set_celld", p_vec, cell_old);
+ undo_redo->add_do_method(node, "set_celld", p_vec, cell_new);
+}
- if (item == -1)
- return TileMap::INVALID_CELL;
+void TileMapEditor::_start_undo(const String &p_action) {
- return palette->get_item_metadata(item);
+ undo_data.clear();
+ undo_redo->create_action(p_action);
}
-void TileMapEditor::set_selected_tile(int p_tile) {
+void TileMapEditor::_finish_undo() {
- int idx = palette->find_metadata(p_tile);
+ if (undo_data.size()) {
+ for (Map<Point2i, CellOp>::Element *E = undo_data.front(); E; E = E->next()) {
+ _create_set_cell_undo(E->key(), E->get(), _get_op_from_cell(E->key()));
+ }
- if (idx >= 0) {
- palette->select(idx, true);
- palette->ensure_current_is_visible();
+ undo_data.clear();
}
+
+ undo_redo->commit_action();
}
-void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h, bool p_flip_v, bool p_transpose) {
+void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h, bool p_flip_v, bool p_transpose) {
ERR_FAIL_COND(!node);
+ if (p_values.size() == 0)
+ return;
+
+ int p_value = p_values[Math::rand() % p_values.size()];
int prev_val = node->get_cell(p_pos.x, p_pos.y);
bool prev_flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
@@ -234,6 +286,15 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h,
if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose && prev_position == position)
return; //check that it's actually different
+ for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) {
+ for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) {
+ Point2i p = Point2i(x, y);
+ if (!undo_data.has(p)) {
+ undo_data[p] = _get_op_from_cell(p);
+ }
+ }
+ }
+
node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose);
if (manual_autotile) {
if (current != -1) {
@@ -292,7 +353,7 @@ void TileMapEditor::_update_palette() {
if (!node)
return;
- int selected = get_selected_tile();
+ Vector<int> selected = get_selected_tiles();
palette->clear();
manual_palette->clear();
manual_palette->hide();
@@ -381,14 +442,17 @@ void TileMapEditor::_update_palette() {
palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
}
- if (selected != -1)
- set_selected_tile(selected);
- else
+ int sel_tile = selected.get(0);
+ if (selected.get(0) != TileMap::INVALID_CELL) {
+ set_selected_tiles(selected);
+ sel_tile = selected.get(Math::rand() % selected.size());
+ } else {
palette->select(0);
+ }
- if (manual_autotile && tileset->tile_get_tile_mode(get_selected_tile()) == TileSet::AUTO_TILE) {
+ if (manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) {
- const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(get_selected_tile());
+ const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(sel_tile);
Vector<Vector2> entries;
for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) {
@@ -396,7 +460,7 @@ void TileMapEditor::_update_palette() {
}
entries.sort();
- Ref<Texture> tex = tileset->tile_get_texture(get_selected_tile());
+ Ref<Texture> tex = tileset->tile_get_texture(sel_tile);
for (int i = 0; i < entries.size(); i++) {
@@ -404,9 +468,9 @@ void TileMapEditor::_update_palette() {
if (tex.is_valid()) {
- Rect2 region = tileset->tile_get_region(get_selected_tile());
- int spacing = tileset->autotile_get_spacing(get_selected_tile());
- region.size = tileset->autotile_get_size(get_selected_tile());
+ Rect2 region = tileset->tile_get_region(sel_tile);
+ int spacing = tileset->autotile_get_spacing(sel_tile);
+ region.size = tileset->autotile_get_size(sel_tile); // !!
region.position += (region.size + Vector2(spacing, spacing)) * entries[i];
if (!region.has_no_area())
@@ -441,7 +505,10 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
_update_palette();
}
- set_selected_tile(id);
+ Vector<int> selected;
+
+ selected.push_back(id);
+ set_selected_tiles(selected);
mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y));
mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y));
@@ -454,18 +521,21 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) {
int prev_id = node->get_cell(p_start.x, p_start.y);
- int id = TileMap::INVALID_CELL;
+ Vector<int> ids;
+ ids.push_back(TileMap::INVALID_CELL);
if (!erase) {
- id = get_selected_tile();
+ ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 0 && ids[0] == TileMap::INVALID_CELL)
return PoolVector<Vector2>();
} else if (prev_id == TileMap::INVALID_CELL) {
return PoolVector<Vector2>();
}
- if (id == prev_id) {
- return PoolVector<Vector2>();
+ for (int i = ids.size() - 1; i >= 0; i--) {
+ if (ids[i] == prev_id) {
+ return PoolVector<Vector2>();
+ }
}
Rect2i r = node->_edit_get_rect();
@@ -555,13 +625,13 @@ void TileMapEditor::_fill_points(const PoolVector<Vector2> p_points, const Dicti
int len = p_points.size();
PoolVector<Vector2>::Read pr = p_points.read();
- int id = p_op["id"];
+ Vector<int> ids = p_op["id"];
bool xf = p_op["flip_h"];
bool yf = p_op["flip_v"];
bool tr = p_op["transpose"];
for (int i = 0; i < len; i++) {
- _set_cell(pr[i], id, xf, yf, tr);
+ _set_cell(pr[i], ids, xf, yf, tr);
node->make_bitmask_area_dirty(pr[i]);
}
node->update_dirty_bitmask();
@@ -574,7 +644,7 @@ void TileMapEditor::_erase_points(const PoolVector<Vector2> p_points) {
for (int i = 0; i < len; i++) {
- _set_cell(pr[i], TileMap::INVALID_CELL);
+ _set_cell(pr[i], invalid_cell);
}
}
@@ -838,14 +908,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_PAINTING) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
tool = TOOL_PAINTING;
- undo_redo->create_action(TTR("Paint TileMap"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Paint TileMap"));
}
} else if (tool == TOOL_PICKING) {
@@ -864,30 +933,27 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_PAINTING) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _set_cell(over_tile, id, flip_h, flip_v, transpose);
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _set_cell(over_tile, ids, flip_h, flip_v, transpose);
+ _finish_undo();
paint_undo.clear();
}
} else if (tool == TOOL_LINE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- undo_redo->create_action(TTR("Line Draw"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Line Draw"));
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), id, flip_h, flip_v, transpose);
+ _set_cell(E->key(), ids, flip_h, flip_v, transpose);
}
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
paint_undo.clear();
@@ -895,35 +961,34 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
} else if (tool == TOOL_RECTANGLE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- undo_redo->create_action(TTR("Rectangle Paint"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Rectangle Paint"));
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), id, flip_h, flip_v, transpose);
+ _set_cell(Point2i(j, i), ids, flip_h, flip_v, transpose);
}
}
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
canvas_item_editor->update();
}
} else if (tool == TOOL_DUPLICATING) {
Point2 ofs = over_tile - rectangle.position;
+ Vector<int> ids;
- undo_redo->create_action(TTR("Duplicate"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Duplicate"));
+ ids.push_back(0);
for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ ids[0] = E->get().cell;
+ _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose);
}
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
copydata.clear();
@@ -931,21 +996,22 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_MOVING) {
Point2 ofs = over_tile - rectangle.position;
+ Vector<int> ids;
- undo_redo->create_action(TTR("Move"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Move"));
+ ids.push_back(TileMap::INVALID_CELL);
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ _set_cell(Point2i(j, i), ids, false, false, false);
}
}
for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ ids[0] = E->get().cell;
+ _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose);
}
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
copydata.clear();
selection_active = false;
@@ -964,17 +1030,15 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return false;
undo_redo->create_action(TTR("Bucket Fill"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
Dictionary op;
- op["id"] = get_selected_tile();
+ op["id"] = get_selected_tiles();
op["flip_h"] = flip_h;
op["flip_v"] = flip_v;
op["transpose"] = transpose;
_fill_points(points, op);
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
// We want to keep the bucket-tool active
@@ -1026,8 +1090,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2 local = node->world_to_map(xform_inv.xform(mb->get_position()));
- undo_redo->create_action(TTR("Erase TileMap"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ _start_undo(TTR("Erase TileMap"));
if (mb->get_shift()) {
#ifdef APPLE_STYLE_KEYS
@@ -1045,7 +1108,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_ERASING;
- _set_cell(local, TileMap::INVALID_CELL);
+ _set_cell(local, invalid_cell);
}
return true;
@@ -1054,8 +1117,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else {
if (tool == TOOL_ERASING || tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
+ _finish_undo();
if (tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
canvas_item_editor->update();
@@ -1067,8 +1129,10 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_BUCKET) {
+ Vector<int> ids;
+ ids.push_back(node->get_cell(over_tile.x, over_tile.y));
Dictionary pop;
- pop["id"] = node->get_cell(over_tile.x, over_tile.y);
+ pop["id"] = ids;
pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
@@ -1116,7 +1180,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
// Paint using bresenham line to prevent holes in painting if the user moves fast
Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
for (int i = 0; i < points.size(); ++i) {
@@ -1126,7 +1190,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
paint_undo[pos] = _get_op_from_cell(pos);
}
- _set_cell(pos, id, flip_h, flip_v, transpose);
+ _set_cell(pos, ids, flip_h, flip_v, transpose);
}
return true;
@@ -1142,7 +1206,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2i pos = points[i];
- _set_cell(pos, TileMap::INVALID_CELL);
+ _set_cell(pos, invalid_cell);
}
return true;
@@ -1157,20 +1221,23 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_LINE_PAINT || tool == TOOL_LINE_ERASE) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
+ Vector<int> tmp_cell;
bool erasing = (tool == TOOL_LINE_ERASE);
+ tmp_cell.push_back(0);
if (erasing && paint_undo.size()) {
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ tmp_cell[0] = E->get().idx;
+ _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
}
}
paint_undo.clear();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
Vector<Point2i> points = line(rectangle_begin.x, over_tile.x, rectangle_begin.y, over_tile.y);
@@ -1179,7 +1246,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
paint_undo[points[i]] = _get_op_from_cell(points[i]);
if (erasing)
- _set_cell(points[i], TileMap::INVALID_CELL);
+ _set_cell(points[i], invalid_cell);
}
canvas_item_editor->update();
@@ -1189,6 +1256,9 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
if (tool == TOOL_RECTANGLE_PAINT || tool == TOOL_RECTANGLE_ERASE) {
+ Vector<int> tmp_cell;
+ tmp_cell.push_back(0);
+
_select(rectangle_begin, over_tile);
if (tool == TOOL_RECTANGLE_ERASE) {
@@ -1197,7 +1267,8 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ tmp_cell[0] = E->get().idx;
+ _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
}
}
@@ -1209,7 +1280,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2i tile = Point2i(j, i);
paint_undo[tile] = _get_op_from_cell(tile);
- _set_cell(tile, TileMap::INVALID_CELL);
+ _set_cell(tile, invalid_cell);
}
}
}
@@ -1466,27 +1537,27 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
if (paint_undo.empty())
return;
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL)
return;
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _draw_cell(id, E->key(), flip_h, flip_v, transpose, xform);
+ _draw_cell(ids[0], E->key(), flip_h, flip_v, transpose, xform);
}
} else if (tool == TOOL_RECTANGLE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL)
return;
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
+ _draw_cell(ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform);
}
}
} else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) {
@@ -1524,17 +1595,17 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
} else if (tool == TOOL_BUCKET) {
- int tile = get_selected_tile();
- _draw_fill_preview(tile, over_tile, flip_h, flip_v, transpose, xform);
+ Vector<int> tiles = get_selected_tiles();
+ _draw_fill_preview(tiles[0], over_tile, flip_h, flip_v, transpose, xform);
} else {
- int st = get_selected_tile();
+ Vector<int> st = get_selected_tiles();
- if (st == TileMap::INVALID_CELL)
+ if (st.size() == 1 && st[0] == TileMap::INVALID_CELL)
return;
- _draw_cell(st, over_tile, flip_h, flip_v, transpose, xform);
+ _draw_cell(st[0], over_tile, flip_h, flip_v, transpose, xform);
}
}
}
@@ -1621,6 +1692,7 @@ TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) {
op.yf = true;
if (node->is_cell_transposed(p_pos.x, p_pos.y))
op.tr = true;
+ op.ac = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
}
return op;
}
@@ -1679,6 +1751,9 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
bucket_cache_tile = -1;
bucket_cache_visited = 0;
+ invalid_cell.resize(1);
+ invalid_cell[0] = TileMap::INVALID_CELL;
+
ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE);
ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T);
@@ -1725,6 +1800,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
palette->set_max_columns(0);
palette->set_icon_mode(ItemList::ICON_MODE_TOP);
palette->set_max_text_lines(2);
+ palette->set_select_mode(ItemList::SELECT_MULTI);
palette->connect("item_selected", this, "_palette_selected");
palette_container->add_child(palette);
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index a1f5d93a8d..b8443ca962 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -129,6 +129,7 @@ class TileMapEditor : public VBoxContainer {
bool xf;
bool yf;
bool tr;
+ Vector2 ac;
CellOp() :
idx(TileMap::INVALID_CELL),
@@ -155,6 +156,9 @@ class TileMapEditor : public VBoxContainer {
List<TileData> copydata;
+ Map<Point2i, CellOp> undo_data;
+ Vector<int> invalid_cell;
+
void _pick_tile(const Point2 &p_pos);
PoolVector<Vector2> _bucket_fill(const Point2i &p_start, bool erase = false, bool preview = false);
@@ -170,8 +174,8 @@ class TileMapEditor : public VBoxContainer {
void _update_copydata();
- int get_selected_tile() const;
- void set_selected_tile(int p_tile);
+ Vector<int> get_selected_tiles() const;
+ void set_selected_tiles(Vector<int> p_tile);
void _manual_toggled(bool p_enabled);
void _text_entered(const String &p_text);
@@ -181,7 +185,10 @@ class TileMapEditor : public VBoxContainer {
void _menu_option(int p_option);
void _palette_selected(int index);
- void _set_cell(const Point2i &p_pos, int p_value, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false);
+ void _start_undo(const String &p_action);
+ void _finish_undo();
+ void _create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new);
+ void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false);
void _canvas_mouse_enter();
void _canvas_mouse_exit();
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
new file mode 100644
index 0000000000..b9b8b07a2e
--- /dev/null
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -0,0 +1,1217 @@
+#include "visual_shader_editor_plugin.h"
+
+#include "core/io/resource_loader.h"
+#include "core/project_settings.h"
+#include "editor/editor_properties.h"
+#include "os/input.h"
+#include "os/keyboard.h"
+#include "scene/animation/animation_player.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/panel.h"
+#include "scene/main/viewport.h"
+
+Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("create_editor", p_node);
+ }
+ return NULL;
+}
+
+void VisualShaderNodePlugin::_bind_methods() {
+
+ BIND_VMETHOD(MethodInfo(Variant::OBJECT, "create_editor", PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode")));
+}
+
+///////////////////
+
+void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
+
+ if (p_visual_shader) {
+ visual_shader = Ref<VisualShader>(p_visual_shader);
+ } else {
+ visual_shader.unref();
+ }
+
+ if (visual_shader.is_null()) {
+ hide();
+ } else {
+ _update_graph();
+ }
+}
+
+void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) {
+ if (plugins.find(p_plugin) != -1)
+ return;
+ plugins.push_back(p_plugin);
+}
+
+void VisualShaderEditor::remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) {
+ plugins.erase(p_plugin);
+}
+
+void VisualShaderEditor::add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
+
+ for (int i = 0; i < add_options.size(); i++) {
+ ERR_FAIL_COND(add_options[i].script == p_script);
+ }
+
+ AddOption ao;
+ ao.name = p_name;
+ ao.script = p_script;
+ ao.category = p_category;
+ add_options.push_back(ao);
+
+ _update_options_menu();
+}
+
+void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) {
+
+ for (int i = 0; i < add_options.size(); i++) {
+ if (add_options[i].script == p_script) {
+ add_options.remove(i);
+ return;
+ }
+ }
+
+ _update_options_menu();
+}
+
+void VisualShaderEditor::_update_options_menu() {
+
+ String prev_category;
+ add_node->get_popup()->clear();
+ for (int i = 0; i < add_options.size(); i++) {
+ if (prev_category != add_options[i].category) {
+ add_node->get_popup()->add_separator(add_options[i].category);
+ }
+ add_node->get_popup()->add_item(add_options[i].name, i);
+ prev_category = add_options[i].category;
+ }
+}
+
+Size2 VisualShaderEditor::get_minimum_size() const {
+
+ return Size2(10, 200);
+}
+
+void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) {
+
+ Button *button = Object::cast_to<Button>(obj);
+ if (!button)
+ return;
+
+ Ref<StyleBox> normal = get_stylebox("normal", "Button");
+ button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
+}
+
+static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+ Ref<StyleBoxEmpty> style(memnew(StyleBoxEmpty));
+ style->set_default_margin(MARGIN_LEFT, p_margin_left * EDSCALE);
+ style->set_default_margin(MARGIN_RIGHT, p_margin_right * EDSCALE);
+ style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * EDSCALE);
+ style->set_default_margin(MARGIN_TOP, p_margin_top * EDSCALE);
+ return style;
+}
+
+void VisualShaderEditor::_update_graph() {
+
+ if (updating)
+ return;
+
+ graph->set_scroll_ofs(visual_shader->get_graph_offset() * EDSCALE);
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ graph->clear_connections();
+ //erase all nodes
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ memdelete(graph->get_child(i));
+ i--;
+ }
+ }
+
+ static const Color type_color[3] = {
+ Color::html("#61daf4"),
+ Color::html("#d67dee"),
+ Color::html("#f6a86e")
+ };
+
+ List<VisualShader::Connection> connections;
+ visual_shader->get_node_connections(type, &connections);
+
+ Ref<StyleBoxEmpty> label_style = make_empty_stylebox(2, 1, 2, 1);
+
+ Vector<int> nodes = visual_shader->get_node_list(type);
+
+ for (int n_i = 0; n_i < nodes.size(); n_i++) {
+
+ Vector2 position = visual_shader->get_node_position(type, nodes[n_i]);
+ Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]);
+
+ GraphNode *node = memnew(GraphNode);
+ graph->add_child(node);
+
+ /*if (!vsnode->is_connected("changed", this, "_node_changed")) {
+ vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED);
+ }*/
+
+ node->set_offset(position);
+
+ node->set_title(vsnode->get_caption());
+ node->set_name(itos(nodes[n_i]));
+
+ if (nodes[n_i] >= 2) {
+ node->set_show_close_button(true);
+ node->connect("close_request", this, "_delete_request", varray(nodes[n_i]), CONNECT_DEFERRED);
+ }
+
+ node->connect("dragged", this, "_node_dragged", varray(nodes[n_i]));
+
+ Control *custom_editor = NULL;
+ int port_offset = 0;
+
+ Ref<VisualShaderNodeUniform> uniform = vsnode;
+ if (uniform.is_valid()) {
+ LineEdit *uniform_name = memnew(LineEdit);
+ uniform_name->set_text(uniform->get_uniform_name());
+ node->add_child(uniform_name);
+ uniform_name->connect("text_entered", this, "_line_edit_changed", varray(uniform_name, nodes[n_i]));
+ uniform_name->connect("focus_exited", this, "_line_edit_focus_out", varray(uniform_name, nodes[n_i]));
+
+ if (vsnode->get_input_port_count() == 0 && vsnode->get_output_port_count() == 1 && vsnode->get_output_port_name(0) == "") {
+ //shortcut
+ VisualShaderNode::PortType port_right = vsnode->get_output_port_type(0);
+ node->set_slot(0, false, VisualShaderNode::PORT_TYPE_SCALAR, Color(), true, port_right, type_color[port_right]);
+ continue;
+ }
+ port_offset++;
+ }
+
+ for (int i = 0; i < plugins.size(); i++) {
+ custom_editor = plugins[i]->create_editor(vsnode);
+ if (custom_editor) {
+ break;
+ }
+ }
+
+ if (custom_editor && vsnode->get_output_port_count() > 0 && vsnode->get_output_port_name(0) == "" && (vsnode->get_input_port_count() == 0 || vsnode->get_input_port_name(0) == "")) {
+ //will be embedded in first port
+ } else if (custom_editor) {
+ port_offset++;
+ node->add_child(custom_editor);
+ custom_editor = NULL;
+ }
+
+ for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) {
+
+ if (vsnode->is_port_separator(i)) {
+ node->add_child(memnew(HSeparator));
+ port_offset++;
+ }
+
+ bool valid_left = i < vsnode->get_input_port_count();
+ VisualShaderNode::PortType port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ bool port_left_used = false;
+ String name_left;
+ if (valid_left) {
+ name_left = vsnode->get_input_port_name(i);
+ port_left = vsnode->get_input_port_type(i);
+ for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
+ if (E->get().to_node == nodes[n_i] && E->get().to_port == i) {
+ port_left_used = true;
+ }
+ }
+ }
+
+ bool valid_right = i < vsnode->get_output_port_count();
+ VisualShaderNode::PortType port_right = VisualShaderNode::PORT_TYPE_SCALAR;
+ String name_right;
+ if (valid_right) {
+ name_right = vsnode->get_output_port_name(i);
+ port_right = vsnode->get_output_port_type(i);
+ }
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+
+ Variant default_value;
+
+ if (valid_left && !port_left_used) {
+ default_value = vsnode->get_input_port_default_value(i);
+ }
+
+ if (default_value.get_type() != Variant::NIL) { // only a label
+ Button *button = memnew(Button);
+ hb->add_child(button);
+ button->connect("pressed", this, "_edit_port_default_input", varray(button, nodes[n_i], i));
+
+ switch (default_value.get_type()) {
+
+ case Variant::COLOR: {
+ button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
+ button->connect("draw", this, "_draw_color_over_button", varray(button, default_value));
+ } break;
+ case Variant::INT:
+ case Variant::REAL: {
+ button->set_text(String::num(default_value, 4));
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 v = default_value;
+ button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3));
+ } break;
+ default: {}
+ }
+ }
+
+ if (i == 0 && custom_editor) {
+
+ hb->add_child(custom_editor);
+ custom_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ } else {
+
+ if (valid_left) {
+
+ Label *label = memnew(Label);
+ label->set_text(name_left);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
+
+ hb->add_spacer();
+
+ if (valid_right) {
+
+ Label *label = memnew(Label);
+ label->set_text(name_right);
+ label->set_align(Label::ALIGN_RIGHT);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
+ }
+
+ if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT) {
+ TextureButton *preview = memnew(TextureButton);
+ preview->set_toggle_mode(true);
+ preview->set_normal_texture(get_icon("GuiVisibilityHidden", "EditorIcons"));
+ preview->set_pressed_texture(get_icon("GuiVisibilityVisible", "EditorIcons"));
+ preview->set_v_size_flags(SIZE_SHRINK_CENTER);
+
+ if (vsnode->get_output_port_for_preview() == i) {
+ preview->set_pressed(true);
+ }
+
+ preview->connect("pressed", this, "_preview_select_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(preview);
+ }
+
+ node->add_child(hb);
+
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
+ }
+
+ if (vsnode->get_output_port_for_preview() >= 0) {
+ VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview);
+ port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview());
+ port_preview->set_h_size_flags(SIZE_SHRINK_CENTER);
+ node->add_child(port_preview);
+ }
+
+ String error = vsnode->get_warning(visual_shader->get_mode(), type);
+ if (error != String()) {
+ Label *error_label = memnew(Label);
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ error_label->set_text(error);
+ node->add_child(error_label);
+ }
+ }
+
+ for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
+
+ int from = E->get().from_node;
+ int from_idx = E->get().from_port;
+ int to = E->get().to_node;
+ int to_idx = E->get().to_port;
+
+ graph->connect_node(itos(from), from_idx, itos(to), to_idx);
+ }
+}
+
+void VisualShaderEditor::_preview_select_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ if (node->get_output_port_for_preview() == p_port) {
+ p_port = -1; //toggle it
+ }
+ undo_redo->create_action("Set Uniform Name");
+ undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", node->get_output_port_for_preview());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_line_edit_changed(const String &p_text, Object *line_edit, int p_node_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeUniform> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ String validated_name = visual_shader->validate_uniform_name(p_text, node);
+
+ updating = true;
+ undo_redo->create_action("Set Uniform Name");
+ undo_redo->add_do_method(node.ptr(), "set_uniform_name", validated_name);
+ undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ Object::cast_to<LineEdit>(line_edit)->set_text(validated_name);
+}
+
+void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) {
+
+ String text = Object::cast_to<LineEdit>(line_edit)->get_text();
+ _line_edit_changed(text, line_edit, p_node_id);
+}
+
+void VisualShaderEditor::_port_edited() {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Variant value = property_editor->get_variant();
+ Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node);
+ ERR_FAIL_COND(!vsn.is_valid());
+
+ undo_redo->create_action("Set Input Default Port");
+ undo_redo->add_do_method(vsn.ptr(), "set_input_port_default_value", editing_port, value);
+ undo_redo->add_undo_method(vsn.ptr(), "set_input_port_default_value", editing_port, vsn->get_input_port_default_value(editing_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+
+ property_editor->hide();
+}
+
+void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNode> vsn = visual_shader->get_node(type, p_node);
+
+ Button *button = Object::cast_to<Button>(p_button);
+ ERR_FAIL_COND(!button);
+ Variant value = vsn->get_input_port_default_value(p_port);
+ property_editor->set_global_position(button->get_global_position() + Vector2(0, button->get_size().height));
+ property_editor->edit(NULL, "", value.get_type(), value, 0, "");
+ property_editor->popup();
+ editing_node = p_node;
+ editing_port = p_port;
+}
+
+void VisualShaderEditor::_add_node(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, add_options.size());
+
+ Ref<VisualShaderNode> vsnode;
+
+ if (add_options[p_idx].type != String()) {
+ VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type));
+ ERR_FAIL_COND(!vsn);
+ vsnode = Ref<VisualShaderNode>(vsn);
+ } else {
+ ERR_FAIL_COND(add_options[p_idx].script.is_null());
+ String base_type = add_options[p_idx].script->get_instance_base_type();
+ VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(base_type));
+ ERR_FAIL_COND(!vsn);
+ vsnode = Ref<VisualShaderNode>(vsn);
+ vsnode->set_script(add_options[p_idx].script.get_ref_ptr());
+ }
+
+ Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE;
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int id_to_use = visual_shader->get_valid_node_id(type);
+
+ undo_redo->create_action("Add Node to Visual Shader");
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use);
+ undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ updating = true;
+ undo_redo->create_action("Node Moved");
+ undo_redo->add_do_method(visual_shader.ptr(), "set_node_position", type, p_node, p_to);
+ undo_redo->add_undo_method(visual_shader.ptr(), "set_node_position", type, p_node, p_from);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void VisualShaderEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int from = p_from.to_int();
+ int to = p_to.to_int();
+
+ if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) {
+ EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid."));
+ return;
+ }
+
+ undo_redo->create_action("Nodes Connected");
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
+
+ graph->disconnect_node(p_from, p_from_index, p_to, p_to_index);
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int from = p_from.to_int();
+ int to = p_to.to_int();
+
+ //updating = true; seems graph edit can handle this, no need to protect
+ undo_redo->create_action("Nodes Disconnected");
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ //updating = false;
+}
+
+void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {
+}
+
+void VisualShaderEditor::_delete_request(int which) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ undo_redo->create_action("Delete Node");
+ undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which);
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which);
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().from_node == which || E->get().to_node == which) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_node_selected(Object *p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ GraphNode *gn = Object::cast_to<GraphNode>(p_node);
+ ERR_FAIL_COND(!gn);
+
+ int id = String(gn->get_name()).to_int();
+
+ Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
+ ERR_FAIL_COND(!vsnode.is_valid());
+
+ //do not rely on this, makes editor more complex
+ //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true);
+}
+
+void VisualShaderEditor::_input(const Ref<InputEvent> p_event) {
+ if (graph->has_focus()) {
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ add_node->get_popup()->set_position(get_viewport()->get_mouse_position());
+ add_node->get_popup()->show_modal();
+ }
+ }
+}
+
+void VisualShaderEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+
+ error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+ }
+}
+
+void VisualShaderEditor::_scroll_changed(const Vector2 &p_scroll) {
+ if (updating)
+ return;
+ updating = true;
+ visual_shader->set_graph_offset(p_scroll / EDSCALE);
+ updating = false;
+}
+
+void VisualShaderEditor::_node_changed(int p_id) {
+ if (updating)
+ return;
+
+ if (is_visible_in_tree()) {
+ _update_graph();
+ }
+}
+
+void VisualShaderEditor::_duplicate_nodes() {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ List<int> nodes;
+
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ int id = String(graph->get_child(i)->get_name()).to_int();
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
+ Ref<VisualShaderNodeOutput> output = node;
+ if (output.is_valid()) //cant duplicate output
+ continue;
+ if (node.is_valid()) {
+ nodes.push_back(id);
+ }
+ }
+ }
+
+ if (nodes.empty())
+ return;
+
+ undo_redo->create_action("Duplicate Nodes");
+
+ int base_id = visual_shader->get_valid_node_id(type);
+ int id_from = base_id;
+ Map<int, int> connection_remap;
+
+ for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
+
+ connection_remap[E->get()] = id_from;
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, E->get());
+
+ Ref<VisualShaderNode> dupli = node->duplicate();
+
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from);
+ undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
+
+ id_from++;
+ }
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) {
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port);
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+
+ //reselect
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ int id = String(graph->get_child(i)->get_name()).to_int();
+ if (nodes.find(id)) {
+ Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(true);
+ } else {
+ Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(false);
+ }
+ }
+ }
+}
+
+void VisualShaderEditor::_mode_selected(int p_id) {
+ _update_graph();
+}
+
+void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, String name) {
+
+ String prev_name = input->get_input_name();
+
+ if (name == prev_name)
+ return;
+
+ bool type_changed = input->get_input_type_by_name(name) != input->get_input_type_by_name(prev_name);
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("Visual Shader Input Type Changed");
+
+ undo_redo->add_do_method(input.ptr(), "set_input_name", name);
+ undo_redo->add_undo_method(input.ptr(), "set_input_name", prev_name);
+
+ if (type_changed) {
+ //restore connections if type changed
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ int id = visual_shader->find_node_id(type, input);
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().from_node == id) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph");
+ undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_bind_methods() {
+
+ ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph);
+ ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node);
+ ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged);
+ ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request);
+ ClassDB::bind_method("_disconnection_request", &VisualShaderEditor::_disconnection_request);
+ ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected);
+ ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed);
+ ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request);
+ ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed);
+ ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input);
+ ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited);
+ ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty);
+ ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out);
+ ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed);
+ ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes);
+ ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected);
+ ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item);
+ ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port);
+ ClassDB::bind_method("_input", &VisualShaderEditor::_input);
+}
+
+VisualShaderEditor *VisualShaderEditor::singleton = NULL;
+
+VisualShaderEditor::VisualShaderEditor() {
+
+ singleton = this;
+ updating = false;
+
+ graph = memnew(GraphEdit);
+ add_child(graph);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM);
+ //graph->add_valid_left_disconnect_type(0);
+ graph->set_v_size_flags(SIZE_EXPAND_FILL);
+ graph->connect("connection_request", this, "_connection_request", varray(), CONNECT_DEFERRED);
+ graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED);
+ graph->connect("node_selected", this, "_node_selected");
+ graph->connect("scroll_offset_changed", this, "_scroll_changed");
+ graph->connect("duplicate_nodes_request", this, "_duplicate_nodes");
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM);
+
+ VSeparator *vs = memnew(VSeparator);
+ graph->get_zoom_hbox()->add_child(vs);
+ graph->get_zoom_hbox()->move_child(vs, 0);
+
+ edit_type = memnew(OptionButton);
+ edit_type->add_item(TTR("Vertex"));
+ edit_type->add_item(TTR("Fragment"));
+ edit_type->add_item(TTR("Light"));
+ edit_type->select(1);
+ edit_type->connect("item_selected", this, "_mode_selected");
+ graph->get_zoom_hbox()->add_child(edit_type);
+ graph->get_zoom_hbox()->move_child(edit_type, 0);
+
+ add_node = memnew(MenuButton);
+ graph->get_zoom_hbox()->add_child(add_node);
+ add_node->set_text(TTR("Add Node.."));
+ graph->get_zoom_hbox()->move_child(add_node, 0);
+ add_node->get_popup()->connect("id_pressed", this, "_add_node");
+
+ add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant"));
+ add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant"));
+ add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant"));
+ add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant"));
+ add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture"));
+ add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap"));
+ add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp"));
+ add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp"));
+ add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp"));
+ add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult"));
+ add_options.push_back(AddOption("TransformVectorMult", "Operators", "VisualShaderNodeTransformVecMult"));
+ add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc"));
+ add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc"));
+ add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct"));
+ add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen"));
+ add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp"));
+ add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp"));
+ add_options.push_back(AddOption("VectorCompose", "Compose", "VisualShaderNodeVectorCompose"));
+ add_options.push_back(AddOption("TransformCompose", "Compose", "VisualShaderNodeTransformCompose"));
+ add_options.push_back(AddOption("VectorDecompose", "Decompose", "VisualShaderNodeVectorDecompose"));
+ add_options.push_back(AddOption("TransformDecompose", "Decompose", "VisualShaderNodeTransformDecompose"));
+ add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform"));
+ add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform"));
+ add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform"));
+ add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform"));
+ add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform"));
+ add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform"));
+ add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput"));
+
+ _update_options_menu();
+
+ error_panel = memnew(PanelContainer);
+ add_child(error_panel);
+ error_label = memnew(Label);
+ error_panel->add_child(error_label);
+ error_label->set_text("eh");
+ error_panel->hide();
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ Ref<VisualShaderNodePluginDefault> default_plugin;
+ default_plugin.instance();
+ add_plugin(default_plugin);
+
+ property_editor = memnew(CustomPropertyEditor);
+ add_child(property_editor);
+
+ property_editor->connect("variant_changed", this, "_port_edited");
+}
+
+void VisualShaderEditorPlugin::edit(Object *p_object) {
+
+ visual_shader_editor->edit(Object::cast_to<VisualShader>(p_object));
+}
+
+bool VisualShaderEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("VisualShader");
+}
+
+void VisualShaderEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ //editor->hide_animation_player_editors();
+ //editor->animation_panel_make_visible(true);
+ button->show();
+ editor->make_bottom_panel_item_visible(visual_shader_editor);
+ visual_shader_editor->set_process_input(true);
+ //visual_shader_editor->set_process(true);
+ } else {
+
+ if (visual_shader_editor->is_visible_in_tree())
+ editor->hide_bottom_panel();
+ button->hide();
+ visual_shader_editor->set_process_input(false);
+ //visual_shader_editor->set_process(false);
+ }
+}
+
+VisualShaderEditorPlugin::VisualShaderEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ visual_shader_editor = memnew(VisualShaderEditor);
+ visual_shader_editor->set_custom_minimum_size(Size2(0, 300));
+
+ button = editor->add_bottom_panel_item(TTR("VisualShader"), visual_shader_editor);
+ button->hide();
+}
+
+VisualShaderEditorPlugin::~VisualShaderEditorPlugin() {
+}
+
+////////////////
+
+class VisualShaderNodePluginInputEditor : public OptionButton {
+ GDCLASS(VisualShaderNodePluginInputEditor, OptionButton)
+
+ Ref<VisualShaderNodeInput> input;
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method("_item_selected", &VisualShaderNodePluginInputEditor::_item_selected);
+ }
+
+public:
+ void _notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+ connect("item_selected", this, "_item_selected");
+ }
+ }
+
+ void _item_selected(int p_item) {
+ VisualShaderEditor::get_singleton()->call_deferred("_input_select_item", input, get_item_text(p_item));
+ }
+
+ void setup(const Ref<VisualShaderNodeInput> &p_input) {
+ input = p_input;
+ Ref<Texture> type_icon[3] = {
+ EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"),
+ EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"),
+ EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"),
+ };
+
+ add_item("[None]");
+ int to_select = -1;
+ for (int i = 0; i < input->get_input_index_count(); i++) {
+ if (input->get_input_name() == input->get_input_index_name(i)) {
+ to_select = i + 1;
+ }
+ add_icon_item(type_icon[input->get_input_index_type(i)], input->get_input_index_name(i));
+ }
+
+ if (to_select >= 0) {
+ select(to_select);
+ }
+ }
+};
+
+class VisualShaderNodePluginDefaultEditor : public VBoxContainer {
+ GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer)
+public:
+ void _property_changed(const String &prop, const Variant &p_value) {
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ updating = true;
+ undo_redo->create_action("Edit Visual Property: " + prop, UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(node.ptr(), prop, p_value);
+ undo_redo->add_undo_property(node.ptr(), prop, node->get(prop));
+ undo_redo->commit_action();
+ updating = false;
+ }
+
+ void _node_changed() {
+ if (updating)
+ return;
+ for (int i = 0; i < properties.size(); i++) {
+ properties[i]->update_property();
+ }
+ }
+
+ void _refresh_request() {
+ VisualShaderEditor::get_singleton()->call_deferred("_update_graph");
+ }
+
+ bool updating;
+ Ref<VisualShaderNode> node;
+ Vector<EditorProperty *> properties;
+
+ void setup(Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, Ref<VisualShaderNode> p_node) {
+ updating = false;
+ node = p_node;
+ properties = p_properties;
+
+ for (int i = 0; i < p_properties.size(); i++) {
+
+ add_child(p_properties[i]);
+
+ properties[i]->connect("property_changed", this, "_property_changed");
+ properties[i]->set_object_and_property(node.ptr(), p_names[i]);
+ properties[i]->update_property();
+ properties[i]->set_name_split_ratio(0);
+ }
+ node->connect("changed", this, "_node_changed");
+ node->connect("editor_refresh_request", this, "_refresh_request", varray(), CONNECT_DEFERRED);
+ }
+
+ static void _bind_methods() {
+ ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed);
+ ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed);
+ ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request);
+ }
+};
+
+Control *VisualShaderNodePluginDefault::create_editor(const Ref<VisualShaderNode> &p_node) {
+
+ if (p_node->is_class("VisualShaderNodeInput")) {
+ //create input
+ VisualShaderNodePluginInputEditor *input_editor = memnew(VisualShaderNodePluginInputEditor);
+ input_editor->setup(p_node);
+ return input_editor;
+ }
+
+ Vector<StringName> properties = p_node->get_editable_properties();
+ if (properties.size() == 0) {
+ return NULL;
+ }
+
+ List<PropertyInfo> props;
+ p_node->get_property_list(&props);
+
+ Vector<PropertyInfo> pinfo;
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ for (int i = 0; i < properties.size(); i++) {
+ if (E->get().name == String(properties[i])) {
+ pinfo.push_back(E->get());
+ }
+ }
+ }
+
+ if (pinfo.size() == 0)
+ return NULL;
+
+ properties.clear();
+
+ Ref<VisualShaderNode> node = p_node;
+ Vector<EditorProperty *> editors;
+
+ for (int i = 0; i < pinfo.size(); i++) {
+
+ EditorProperty *prop = EditorInspector::instantiate_property_editor(node.ptr(), pinfo[i].type, pinfo[i].name, pinfo[i].hint, pinfo[i].hint_string, pinfo[i].usage);
+ if (!prop)
+ return NULL;
+
+ if (Object::cast_to<EditorPropertyResource>(prop)) {
+ Object::cast_to<EditorPropertyResource>(prop)->set_use_sub_inspector(false);
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyTransform>(prop)) {
+ prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyFloat>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) {
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyEnum>(prop)) {
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ Object::cast_to<EditorPropertyEnum>(prop)->set_option_button_clip(false);
+ }
+
+ editors.push_back(prop);
+ properties.push_back(pinfo[i].name);
+ }
+ VisualShaderNodePluginDefaultEditor *editor = memnew(VisualShaderNodePluginDefaultEditor);
+ editor->setup(editors, properties, p_node);
+ return editor;
+}
+
+void EditorPropertyShaderMode::_option_selected(int p_which) {
+
+ //will not use this, instead will do all the logic setting manually
+ //emit_signal("property_changed", get_edited_property(), p_which);
+
+ Ref<VisualShader> visual_shader(Object::cast_to<VisualShader>(get_edited_object()));
+
+ if (visual_shader->get_mode() == p_which)
+ return;
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("Visual Shader Mode Changed");
+ //do is easy
+ undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which);
+ undo_redo->add_undo_method(visual_shader.ptr(), "set_mode", visual_shader->get_mode());
+ //now undo is hell
+
+ //1. restore connections to output
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+
+ VisualShader::Type type = VisualShader::Type(i);
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().to_node == VisualShader::NODE_ID_OUTPUT) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+ }
+ //2. restore input indices
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+
+ VisualShader::Type type = VisualShader::Type(i);
+ Vector<int> nodes = visual_shader->get_node_list(type);
+ for (int i = 0; i < nodes.size(); i++) {
+ Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[i]);
+ if (!input.is_valid()) {
+ continue;
+ }
+
+ undo_redo->add_undo_method(input.ptr(), "set_input_name", input->get_input_name());
+ }
+ }
+
+ //3. restore enums and flags
+ List<PropertyInfo> props;
+ visual_shader->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ if (E->get().name.begins_with("flags/") || E->get().name.begins_with("modes/")) {
+ undo_redo->add_undo_property(visual_shader.ptr(), E->get().name, visual_shader->get(E->get().name));
+ }
+ }
+
+ //update graph
+ undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph");
+ undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph");
+
+ undo_redo->commit_action();
+}
+
+void EditorPropertyShaderMode::update_property() {
+
+ int which = get_edited_object()->get(get_edited_property());
+ options->select(which);
+}
+
+void EditorPropertyShaderMode::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+void EditorPropertyShaderMode::set_option_button_clip(bool p_enable) {
+ options->set_clip_text(p_enable);
+}
+
+void EditorPropertyShaderMode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyShaderMode::_option_selected);
+}
+
+EditorPropertyShaderMode::EditorPropertyShaderMode() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+
+bool EditorInspectorShaderModePlugin::can_handle(Object *p_object) {
+ return true; //can handle everything
+}
+
+void EditorInspectorShaderModePlugin::parse_begin(Object *p_object) {
+ //do none
+}
+
+bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ if (p_path == "mode" && p_object->is_class("VisualShader") && p_type == Variant::INT) {
+
+ EditorPropertyShaderMode *editor = memnew(EditorPropertyShaderMode);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ return true;
+ }
+
+ return false; //can be overriden, although it will most likely be last anyway
+}
+
+void EditorInspectorShaderModePlugin::parse_end() {
+ //do none
+}
+//////////////////////////////////
+
+void VisualShaderNodePortPreview::_shader_changed() {
+ if (shader.is_null()) {
+ return;
+ }
+
+ Vector<VisualShader::DefaultTextureParam> default_textures;
+ String shader_code = shader->generate_preview_shader(type, node, port, default_textures);
+
+ Ref<Shader> preview_shader;
+ preview_shader.instance();
+ preview_shader->set_code(shader_code);
+ for (int i = 0; i < default_textures.size(); i++) {
+ preview_shader->set_default_texture_param(default_textures[i].name, default_textures[i].param);
+ }
+
+ Ref<ShaderMaterial> material;
+ material.instance();
+ material->set_shader(preview_shader);
+
+ //find if a material is also being edited and copy parameters to this one
+
+ for (int i = EditorNode::get_singleton()->get_editor_history()->get_path_size() - 1; i >= 0; i--) {
+ Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_path_object(i));
+ if (!object)
+ continue;
+ ShaderMaterial *src_mat = Object::cast_to<ShaderMaterial>(object);
+ if (src_mat && src_mat->get_shader().is_valid()) {
+
+ List<PropertyInfo> params;
+ src_mat->get_shader()->get_param_list(&params);
+ for (List<PropertyInfo>::Element *E = params.front(); E; E = E->next()) {
+ material->set(E->get().name, src_mat->get(E->get().name));
+ }
+ }
+ }
+
+ set_material(material);
+}
+
+void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port) {
+
+ shader = p_shader;
+ shader->connect("changed", this, "_shader_changed");
+ type = p_type;
+ port = p_port;
+ node = p_node;
+ update();
+ _shader_changed();
+}
+
+Size2 VisualShaderNodePortPreview::get_minimum_size() const {
+ return Size2(100, 100) * EDSCALE;
+}
+
+void VisualShaderNodePortPreview::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+ Vector<Vector2> points;
+ Vector<Vector2> uvs;
+ Vector<Color> colors;
+ points.push_back(Vector2());
+ uvs.push_back(Vector2(0, 0));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(Vector2(get_size().width, 0));
+ uvs.push_back(Vector2(1, 0));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(get_size());
+ uvs.push_back(Vector2(1, 1));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(Vector2(0, get_size().height));
+ uvs.push_back(Vector2(0, 1));
+ colors.push_back(Color(1, 1, 1, 1));
+
+ draw_primitive(points, colors, uvs);
+ }
+}
+
+void VisualShaderNodePortPreview::_bind_methods() {
+ ClassDB::bind_method("_shader_changed", &VisualShaderNodePortPreview::_shader_changed);
+}
+
+VisualShaderNodePortPreview::VisualShaderNodePortPreview() {
+}
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
new file mode 100644
index 0000000000..f86374ff6b
--- /dev/null
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -0,0 +1,187 @@
+#ifndef VISUAL_SHADER_EDITOR_PLUGIN_H
+#define VISUAL_SHADER_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/property_editor.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+#include "scene/resources/visual_shader.h"
+
+class VisualShaderNodePlugin : public Reference {
+
+ GDCLASS(VisualShaderNodePlugin, Reference)
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Control *create_editor(const Ref<VisualShaderNode> &p_node);
+};
+
+class VisualShaderEditor : public VBoxContainer {
+
+ GDCLASS(VisualShaderEditor, VBoxContainer);
+
+ CustomPropertyEditor *property_editor;
+ int editing_node;
+ int editing_port;
+
+ Ref<VisualShader> visual_shader;
+ GraphEdit *graph;
+ MenuButton *add_node;
+
+ OptionButton *edit_type;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ UndoRedo *undo_redo;
+
+ void _update_graph();
+
+ struct AddOption {
+ String name;
+ String category;
+ String type;
+ Ref<Script> script;
+ AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) {
+ name = p_name;
+ type = p_type;
+ category = p_category;
+ }
+ };
+
+ Vector<AddOption> add_options;
+
+ void _draw_color_over_button(Object *obj, Color p_color);
+
+ void _add_node(int p_idx);
+ void _update_options_menu();
+
+ static VisualShaderEditor *singleton;
+
+ void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node);
+ bool updating;
+
+ void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
+ void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
+
+ void _scroll_changed(const Vector2 &p_scroll);
+ void _node_selected(Object *p_node);
+
+ void _delete_request(int);
+
+ void _removed_from_graph();
+
+ void _node_changed(int p_id);
+
+ void _edit_port_default_input(Object *p_button, int p_node, int p_port);
+ void _port_edited();
+
+ void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
+
+ void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id);
+ void _line_edit_focus_out(Object *line_edit, int p_node_id);
+
+ void _duplicate_nodes();
+
+ Vector<Ref<VisualShaderNodePlugin> > plugins;
+
+ void _mode_selected(int p_id);
+
+ void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
+
+ void _preview_select_port(int p_node, int p_port);
+ void _input(const Ref<InputEvent> p_event);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
+ void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
+
+ static VisualShaderEditor *get_singleton() { return singleton; }
+
+ void add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script);
+ void remove_custom_type(const Ref<Script> &p_script);
+
+ virtual Size2 get_minimum_size() const;
+ void edit(VisualShader *p_visual_shader);
+ VisualShaderEditor();
+};
+
+class VisualShaderEditorPlugin : public EditorPlugin {
+
+ GDCLASS(VisualShaderEditorPlugin, EditorPlugin);
+
+ VisualShaderEditor *visual_shader_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ virtual String get_name() const { return "VisualShader"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ VisualShaderEditorPlugin(EditorNode *p_node);
+ ~VisualShaderEditorPlugin();
+};
+
+class VisualShaderNodePluginDefault : public VisualShaderNodePlugin {
+
+ GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin)
+
+public:
+ virtual Control *create_editor(const Ref<VisualShaderNode> &p_node);
+};
+
+class EditorPropertyShaderMode : public EditorProperty {
+ GDCLASS(EditorPropertyShaderMode, EditorProperty)
+ OptionButton *options;
+
+ void _option_selected(int p_which);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ void set_option_button_clip(bool p_enable);
+ EditorPropertyShaderMode();
+};
+
+class EditorInspectorShaderModePlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorShaderModePlugin, EditorInspectorPlugin)
+
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+ virtual void parse_end();
+};
+
+class VisualShaderNodePortPreview : public Control {
+ GDCLASS(VisualShaderNodePortPreview, Control)
+ Ref<VisualShader> shader;
+ VisualShader::Type type;
+ int node;
+ int port;
+ void _shader_changed(); //must regen
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual Size2 get_minimum_size() const;
+ void setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port);
+ VisualShaderNodePortPreview();
+};
+
+#endif // VISUAL_SHADER_EDITOR_PLUGIN_H
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 9f87fc82b5..170546f14c 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -76,6 +76,9 @@ void ProjectExportDialog::popup_export() {
}
_update_presets();
+ if (presets->get_current() >= 0) {
+ _edit_preset(presets->get_current()); // triggers rescan for templates if newly installed
+ }
// Restore valid window bounds or pop up at default size.
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/export_bounds")) {
@@ -154,7 +157,6 @@ void ProjectExportDialog::_update_presets() {
if (current_idx != -1) {
presets->select(current_idx);
- //_edit_preset(current_idx);
}
updating = false;
@@ -167,6 +169,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
name->set_editable(false);
runnable->set_disabled(true);
parameters->edit(NULL);
+ presets->unselect_all();
delete_preset->set_disabled(true);
sections->hide();
patches->clear();
@@ -438,11 +441,9 @@ void ProjectExportDialog::_delete_preset() {
void ProjectExportDialog::_delete_preset_confirm() {
int idx = presets->get_current();
- parameters->edit(NULL); //to avoid crash
_edit_preset(-1);
EditorExport::get_singleton()->remove_export_preset(idx);
_update_presets();
- _edit_preset(presets->get_current());
}
Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 3e17385909..e6ae2d64e7 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -106,6 +106,12 @@ void ProjectSettingsEditor::_notification(int p_what) {
translation_res_file_open->add_filter("*." + E->get());
translation_res_option_file_open->add_filter("*." + E->get());
}
+
+ restart_close_button->set_icon(get_icon("Close", "EditorIcons"));
+ restart_container->add_style_override("panel", get_stylebox("bg", "Tree"));
+ restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons"));
+ restart_label->add_color_override("font_color", get_color("error_color", "Editor"));
+
} break;
case NOTIFICATION_POPUP_HIDE: {
EditorSettings::get_singleton()->set("interface/dialogs/project_settings_bounds", get_rect());
@@ -149,53 +155,71 @@ void ProjectSettingsEditor::_action_edited() {
if (!ti)
return;
- String new_name = ti->get_text(0);
- String old_name = add_at.substr(add_at.find("/") + 1, add_at.length());
+ if (input_editor->get_selected_column() == 0) {
- if (new_name == old_name)
- return;
+ String new_name = ti->get_text(0);
+ String old_name = add_at.substr(add_at.find("/") + 1, add_at.length());
- if (new_name == "" || !_validate_action_name(new_name)) {
+ if (new_name == old_name)
+ return;
- ti->set_text(0, old_name);
- add_at = "input/" + old_name;
+ if (new_name == "" || !_validate_action_name(new_name)) {
- message->set_text(TTR("Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or '\"'."));
- message->popup_centered(Size2(300, 100) * EDSCALE);
- return;
- }
+ ti->set_text(0, old_name);
+ add_at = "input/" + old_name;
- String action_prop = "input/" + new_name;
+ message->set_text(TTR("Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or '\"'"));
+ message->popup_centered(Size2(300, 100) * EDSCALE);
+ return;
+ }
- if (ProjectSettings::get_singleton()->has_setting(action_prop)) {
+ String action_prop = "input/" + new_name;
- ti->set_text(0, old_name);
- add_at = "input/" + old_name;
+ if (ProjectSettings::get_singleton()->has_setting(action_prop)) {
- message->set_text(vformat(TTR("Action '%s' already exists!"), new_name));
- message->popup_centered(Size2(300, 100) * EDSCALE);
- return;
- }
+ ti->set_text(0, old_name);
+ add_at = "input/" + old_name;
- int order = ProjectSettings::get_singleton()->get_order(add_at);
- Dictionary action = ProjectSettings::get_singleton()->get(add_at);
-
- setting = true;
- undo_redo->create_action(TTR("Rename Input Action Event"));
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", add_at);
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_prop, action);
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", action_prop, order);
- undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", action_prop);
- undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", add_at, action);
- undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", add_at, order);
- undo_redo->add_do_method(this, "_update_actions");
- undo_redo->add_undo_method(this, "_update_actions");
- undo_redo->add_do_method(this, "_settings_changed");
- undo_redo->add_undo_method(this, "_settings_changed");
- undo_redo->commit_action();
- setting = false;
+ message->set_text(vformat(TTR("Action '%s' already exists!"), new_name));
+ message->popup_centered(Size2(300, 100) * EDSCALE);
+ return;
+ }
- add_at = action_prop;
+ int order = ProjectSettings::get_singleton()->get_order(add_at);
+ Dictionary action = ProjectSettings::get_singleton()->get(add_at);
+
+ setting = true;
+ undo_redo->create_action(TTR("Rename Input Action Event"));
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", add_at);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_prop, action);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", action_prop, order);
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", action_prop);
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", add_at, action);
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", add_at, order);
+ undo_redo->add_do_method(this, "_update_actions");
+ undo_redo->add_undo_method(this, "_update_actions");
+ undo_redo->add_do_method(this, "_settings_changed");
+ undo_redo->add_undo_method(this, "_settings_changed");
+ undo_redo->commit_action();
+ setting = false;
+
+ add_at = action_prop;
+ } else if (input_editor->get_selected_column() == 1) {
+
+ String name = "input/" + ti->get_text(0);
+ Dictionary old_action = ProjectSettings::get_singleton()->get(name);
+ Dictionary new_action = old_action.duplicate();
+ new_action["deadzone"] = ti->get_range(1);
+
+ undo_redo->create_action(TTR("Change Action deadzone"));
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, new_action);
+ undo_redo->add_do_method(this, "_update_actions");
+ undo_redo->add_do_method(this, "_settings_changed");
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_action);
+ undo_redo->add_undo_method(this, "_update_actions");
+ undo_redo->add_undo_method(this, "_settings_changed");
+ undo_redo->commit_action();
+ }
}
void ProjectSettingsEditor::_device_input_add() {
@@ -237,24 +261,18 @@ void ProjectSettingsEditor::_device_input_add() {
jm->set_axis_value(device_index->get_selected() & 1 ? 1 : -1);
jm->set_device(_get_current_device());
- bool should_update_event = true;
- Variant deadzone = device_special_value->get_value();
for (int i = 0; i < events.size(); i++) {
Ref<InputEventJoypadMotion> aie = events[i];
if (aie.is_null())
continue;
+
if (aie->get_device() == jm->get_device() && aie->get_axis() == jm->get_axis() && aie->get_axis_value() == jm->get_axis_value()) {
- should_update_event = false;
- break;
+ return;
}
}
- if (!should_update_event && deadzone == action["deadzone"])
- return;
-
ie = jm;
- action["deadzone"] = deadzone;
} break;
case INPUT_JOY_BUTTON: {
@@ -382,6 +400,7 @@ void ProjectSettingsEditor::_show_last_added(const Ref<InputEvent> &p_event, con
while (child) {
Variant input = child->get_meta("__input");
if (p_event == input) {
+ r->set_collapsed(false);
child->select(0);
found = true;
break;
@@ -430,8 +449,6 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
press_a_key->grab_focus();
- device_special_value_label->hide();
- device_special_value->hide();
} break;
case INPUT_MOUSE_BUTTON: {
@@ -442,10 +459,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
device_index->add_item(TTR("Middle Button"));
device_index->add_item(TTR("Wheel Up Button"));
device_index->add_item(TTR("Wheel Down Button"));
- device_index->add_item(TTR("Button 6"));
- device_index->add_item(TTR("Button 7"));
- device_index->add_item(TTR("Button 8"));
- device_index->add_item(TTR("Button 9"));
+ device_index->add_item(TTR("Wheel Left Button"));
+ device_index->add_item(TTR("Wheel Right Button"));
+ device_index->add_item(TTR("X Button 1"));
+ device_index->add_item(TTR("X Button 2"));
device_input->popup_centered_minsize(Size2(350, 95) * EDSCALE);
Ref<InputEventMouseButton> mb = p_exiting_event;
@@ -458,8 +475,6 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
device_input->get_ok()->set_text(TTR("Add"));
}
- device_special_value_label->hide();
- device_special_value->hide();
} break;
case INPUT_JOY_MOTION: {
@@ -482,14 +497,6 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
device_input->get_ok()->set_text(TTR("Add"));
}
- device_special_value_label->set_text(TTR("Deadzone (global to the action):"));
- device_special_value_label->show();
- device_special_value->set_min(0.0f);
- device_special_value->set_max(1.0f);
- device_special_value->set_step(0.01f);
- Dictionary action = ProjectSettings::get_singleton()->get(add_at);
- device_special_value->set_value(action.has("deadzone") ? action["deadzone"] : Variant(0.5f));
- device_special_value->show();
} break;
case INPUT_JOY_BUTTON: {
@@ -512,8 +519,6 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
device_input->get_ok()->set_text(TTR("Add"));
}
- device_special_value_label->hide();
- device_special_value->hide();
} break;
default: {}
}
@@ -656,6 +661,14 @@ void ProjectSettingsEditor::_update_actions() {
if (setting)
return;
+ Map<String, bool> collapsed;
+
+ if (input_editor->get_root() && input_editor->get_root()->get_children()) {
+ for (TreeItem *item = input_editor->get_root()->get_children(); item; item = item->get_next()) {
+ collapsed[item->get_text(0)] = item->is_collapsed();
+ }
+ }
+
input_editor->clear();
TreeItem *root = input_editor->create_item();
input_editor->set_hide_root(true);
@@ -673,17 +686,26 @@ void ProjectSettingsEditor::_update_actions() {
if (name == "")
continue;
+ Dictionary action = ProjectSettings::get_singleton()->get(pi.name);
+ Array events = action["events"];
+
TreeItem *item = input_editor->create_item(root);
item->set_text(0, name);
- item->add_button(0, get_icon("Add", "EditorIcons"), 1, false, TTR("Add Event"));
- if (!ProjectSettings::get_singleton()->get_input_presets().find(pi.name)) {
- item->add_button(0, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
- item->set_editable(0, true);
- }
item->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
+ if (collapsed.has(name))
+ item->set_collapsed(collapsed[name]);
- Dictionary action = ProjectSettings::get_singleton()->get(pi.name);
- Array events = action["events"];
+ item->set_editable(1, true);
+ item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
+ item->set_range_config(1, 0.0, 1.0, 0.01);
+ item->set_range(1, action["deadzone"]);
+ item->set_custom_bg_color(1, get_color("prop_subsection", "Editor"));
+
+ item->add_button(2, get_icon("Add", "EditorIcons"), 1, false, TTR("Add Event"));
+ if (!ProjectSettings::get_singleton()->get_input_presets().find(pi.name)) {
+ item->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
+ item->set_editable(2, true);
+ }
for (int i = 0; i < events.size(); i++) {
@@ -752,10 +774,11 @@ void ProjectSettingsEditor::_update_actions() {
action->set_text(0, str);
action->set_icon(0, get_icon("JoyAxis", "EditorIcons"));
}
- action->add_button(0, get_icon("Edit", "EditorIcons"), 3, false, TTR("Edit"));
- action->add_button(0, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
action->set_metadata(0, i);
action->set_meta("__input", event);
+
+ action->add_button(2, get_icon("Edit", "EditorIcons"), 3, false, TTR("Edit"));
+ action->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
}
}
@@ -783,15 +806,13 @@ void ProjectSettingsEditor::popup_project_settings() {
plugin_settings->update_plugins();
}
-void ProjectSettingsEditor::_item_selected() {
+void ProjectSettingsEditor::_item_selected(const String &p_path) {
- TreeItem *ti = globals_editor->get_property_editor()->get_property_tree()->get_selected();
- if (!ti)
- return;
- if (!ti->get_parent())
+ String selected_path = p_path;
+ if (selected_path == String())
return;
category->set_text(globals_editor->get_current_section());
- property->set_text(ti->get_text(0));
+ property->set_text(selected_path);
popup_copy_to_feature->set_disabled(false);
}
@@ -848,7 +869,7 @@ void ProjectSettingsEditor::_item_add() {
void ProjectSettingsEditor::_item_del() {
- String path = globals_editor->get_property_editor()->get_selected_path();
+ String path = globals_editor->get_inspector()->get_selected_path();
if (path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!"));
return;
@@ -1026,7 +1047,7 @@ void ProjectSettingsEditor::_copy_to_platform_about_to_show() {
void ProjectSettingsEditor::_copy_to_platform(int p_which) {
- String path = globals_editor->get_property_editor()->get_selected_path();
+ String path = globals_editor->get_inspector()->get_selected_path();
if (path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!"));
return;
@@ -1555,7 +1576,7 @@ void ProjectSettingsEditor::_update_translations() {
void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) {
- globals_editor->get_property_editor()->set_use_filter(p_pressed);
+ globals_editor->get_inspector()->set_use_filter(p_pressed);
if (p_pressed) {
@@ -1576,7 +1597,7 @@ void ProjectSettingsEditor::_clear_search_box() {
return;
search_box->clear();
- globals_editor->get_property_editor()->update_tree();
+ globals_editor->get_inspector()->update_tree();
}
void ProjectSettingsEditor::set_plugins_page() {
@@ -1589,6 +1610,18 @@ TabContainer *ProjectSettingsEditor::get_tabs() {
return tab_container;
}
+void ProjectSettingsEditor::_editor_restart() {
+ EditorNode::get_singleton()->save_all_scenes_and_restart();
+}
+
+void ProjectSettingsEditor::_editor_restart_request() {
+ restart_container->show();
+}
+
+void ProjectSettingsEditor::_editor_restart_close() {
+ restart_container->hide();
+}
+
void ProjectSettingsEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_item_selected"), &ProjectSettingsEditor::_item_selected);
@@ -1634,6 +1667,10 @@ void ProjectSettingsEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_copy_to_platform_about_to_show"), &ProjectSettingsEditor::_copy_to_platform_about_to_show);
+ ClassDB::bind_method(D_METHOD("_editor_restart_request"), &ProjectSettingsEditor::_editor_restart_request);
+ ClassDB::bind_method(D_METHOD("_editor_restart"), &ProjectSettingsEditor::_editor_restart);
+ ClassDB::bind_method(D_METHOD("_editor_restart_close"), &ProjectSettingsEditor::_editor_restart_close);
+
ClassDB::bind_method(D_METHOD("get_tabs"), &ProjectSettingsEditor::get_tabs);
}
@@ -1720,16 +1757,17 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
search_bar->add_child(clear_button);
clear_button->connect("pressed", this, "_clear_search_box");
- globals_editor = memnew(SectionedPropertyEditor);
+ globals_editor = memnew(SectionedInspector);
props_base->add_child(globals_editor);
- globals_editor->get_property_editor()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
- globals_editor->get_property_editor()->set_property_selectable(true);
+ globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
+ globals_editor->get_inspector()->set_property_selectable(true);
//globals_editor->hide_top_label();
globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
globals_editor->register_search_box(search_box);
- globals_editor->get_property_editor()->get_property_tree()->connect("cell_selected", this, "_item_selected");
- globals_editor->get_property_editor()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
- globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited");
+ globals_editor->get_inspector()->connect("property_selected", this, "_item_selected");
+ //globals_editor->get_inspector()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
+ globals_editor->get_inspector()->connect("property_edited", this, "_settings_prop_edited");
+ globals_editor->get_inspector()->connect("restart_requested", this, "_editor_restart_request");
Button *del = memnew(Button);
hbc->add_child(del);
@@ -1749,6 +1787,26 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
get_ok()->set_text(TTR("Close"));
set_hide_on_ok(true);
+ restart_container = memnew(PanelContainer);
+ props_base->add_child(restart_container);
+ HBoxContainer *restart_hb = memnew(HBoxContainer);
+ restart_container->add_child(restart_hb);
+ restart_icon = memnew(TextureRect);
+ restart_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ restart_hb->add_child(restart_icon);
+ restart_label = memnew(Label);
+ restart_label->set_text(TTR("Editor must be restarted for changes to take effect"));
+ restart_hb->add_child(restart_label);
+ restart_hb->add_spacer();
+ Button *restart_button = memnew(Button);
+ restart_button->connect("pressed", this, "_editor_restart");
+ restart_hb->add_child(restart_button);
+ restart_button->set_text(TTR("Save & Restart"));
+ restart_close_button = memnew(ToolButton);
+ restart_close_button->connect("pressed", this, "_editor_restart_close");
+ restart_hb->add_child(restart_close_button);
+ restart_container->hide();
+
message = memnew(AcceptDialog);
add_child(message);
@@ -1790,6 +1848,14 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
input_editor = memnew(Tree);
vbc->add_child(input_editor);
input_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ input_editor->set_columns(3);
+ input_editor->set_column_titles_visible(true);
+ input_editor->set_column_title(0, TTR("Action"));
+ input_editor->set_column_title(1, TTR("Deadzone"));
+ input_editor->set_column_expand(1, false);
+ input_editor->set_column_min_width(1, 80);
+ input_editor->set_column_expand(2, false);
+ input_editor->set_column_min_width(2, 50);
input_editor->connect("item_edited", this, "_action_edited");
input_editor->connect("item_activated", this, "_action_activated");
input_editor->connect("cell_selected", this, "_action_selected");
@@ -1846,14 +1912,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
device_index = memnew(OptionButton);
vbc_right->add_child(device_index);
- l = memnew(Label);
- l->set_text(TTR("Special value:"));
- vbc_right->add_child(l);
- device_special_value_label = l;
-
- device_special_value = memnew(SpinBox);
- vbc_right->add_child(device_special_value);
-
setting = false;
//translations
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index b8bfdcd876..3b74ae1909 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -35,7 +35,7 @@
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
#include "editor/editor_plugin_settings.h"
-#include "editor/property_editor.h"
+#include "editor/editor_sectioned_inspector.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
@@ -64,7 +64,7 @@ class ProjectSettingsEditor : public AcceptDialog {
EditorData *data;
UndoRedo *undo_redo;
- SectionedPropertyEditor *globals_editor;
+ SectionedInspector *globals_editor;
HBoxContainer *search_bar;
Button *search_button;
@@ -83,8 +83,6 @@ class ProjectSettingsEditor : public AcceptDialog {
OptionButton *device_id;
OptionButton *device_index;
Label *device_index_label;
- SpinBox *device_special_value;
- Label *device_special_value_label;
MenuButton *popup_copy_to_feature;
LineEdit *action_name;
@@ -114,7 +112,7 @@ class ProjectSettingsEditor : public AcceptDialog {
EditorPluginSettings *plugin_settings;
- void _item_selected();
+ void _item_selected(const String &);
void _item_adds(String);
void _item_add();
void _item_del();
@@ -168,6 +166,15 @@ class ProjectSettingsEditor : public AcceptDialog {
static ProjectSettingsEditor *singleton;
+ Label *restart_label;
+ TextureRect *restart_icon;
+ PanelContainer *restart_container;
+ ToolButton *restart_close_button;
+
+ void _editor_restart_request();
+ void _editor_restart();
+ void _editor_restart_close();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index e912ebe03a..7f46844f6c 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -847,6 +847,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
if (!color_picker) {
//late init for performance
color_picker = memnew(ColorPicker);
+ color_picker->set_deferred_mode(true);
add_child(color_picker);
color_picker->hide();
color_picker->connect("color_changed", this, "_color_changed");
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 77ee65879b..2ffaa0ca12 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -551,6 +551,32 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
reparent_dialog->set_current(nodeset);
} break;
+ case TOOL_MAKE_ROOT: {
+
+ List<Node *> nodes = editor_selection->get_selected_node_list();
+ ERR_FAIL_COND(nodes.size() != 1);
+
+ Node *node = nodes.front()->get();
+ Node *root = get_tree()->get_edited_scene_root();
+
+ if (node == root)
+ return;
+
+ editor_data->get_undo_redo().create_action("Make node as Root");
+ _node_replace_owner(root, node, node, MODE_DO);
+ editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node);
+ editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node);
+ editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename());
+
+ editor_data->get_undo_redo().add_undo_method(node, "set_filename", String());
+ editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root);
+ editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node);
+ _node_replace_owner(root, node, root, MODE_UNDO);
+ editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_undo_reference(root);
+ editor_data->get_undo_redo().commit_action();
+ } break;
case TOOL_MULTI_EDIT: {
Node *root = EditorNode::get_singleton()->get_edited_scene();
@@ -757,6 +783,26 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
}
} break;
+ case TOOL_CREATE_2D_SCENE:
+ case TOOL_CREATE_3D_SCENE:
+ case TOOL_CREATE_USER_INTERFACE: {
+
+ Node *new_node;
+ switch (p_tool) {
+ case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break;
+ case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break;
+ case TOOL_CREATE_USER_INTERFACE: new_node = memnew(Control); break;
+ }
+
+ editor_data->get_undo_redo().create_action("New Scene Root");
+ editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", new_node);
+ editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_do_reference(new_node);
+ editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL);
+ editor_data->get_undo_redo().commit_action();
+
+ } break;
+
default: {
if (p_tool >= EDIT_SUBRESOURCE_BASE) {
@@ -803,6 +849,35 @@ void SceneTreeDock::_notification(int p_what) {
EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed");
+ create_root_dialog->add_child(memnew(Label(TTR("Create Root Node:"))));
+
+ Button *button_2d = memnew(Button);
+ create_root_dialog->add_child(button_2d);
+
+ button_2d->set_text(TTR("2D Scene"));
+ button_2d->set_icon(get_icon("Node2D", "EditorIcons"));
+ button_2d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_2D_SCENE, false));
+
+ Button *button_3d = memnew(Button);
+ create_root_dialog->add_child(button_3d);
+ button_3d->set_text(TTR("3D Scene"));
+ button_3d->set_icon(get_icon("Spatial", "EditorIcons"));
+ button_3d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_3D_SCENE, false));
+
+ Button *button_ui = memnew(Button);
+ create_root_dialog->add_child(button_ui);
+ button_ui->set_text(TTR("User Interface"));
+ button_ui->set_icon(get_icon("Control", "EditorIcons"));
+ button_ui->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_USER_INTERFACE, false));
+
+ Button *button_custom = memnew(Button);
+ create_root_dialog->add_child(button_custom);
+ button_custom->set_text(TTR("Custom Node"));
+ button_custom->set_icon(get_icon("Add", "EditorIcons"));
+ button_custom->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false));
+
+ create_root_dialog->add_spacer();
+
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -820,21 +895,49 @@ void SceneTreeDock::_notification(int p_what) {
filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
} break;
+ case NOTIFICATION_PROCESS: {
+
+ bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == NULL;
+
+ if (show_create_root != create_root_dialog->is_visible_in_tree()) {
+ if (show_create_root) {
+ create_root_dialog->show();
+ scene_tree->hide();
+ } else {
+ create_root_dialog->hide();
+ scene_tree->show();
+ }
+ }
+
+ } break;
}
}
-void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root) {
+void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) {
if (p_base != p_node) {
if (p_node->get_owner() == p_base) {
UndoRedo *undo_redo = &editor_data->get_undo_redo();
- undo_redo->add_do_method(p_node, "set_owner", p_root);
- undo_redo->add_undo_method(p_node, "set_owner", p_base);
+ switch (p_mode) {
+ case MODE_BIDI: {
+ undo_redo->add_do_method(p_node, "set_owner", p_root);
+ undo_redo->add_undo_method(p_node, "set_owner", p_base);
+
+ } break;
+ case MODE_DO: {
+ undo_redo->add_do_method(p_node, "set_owner", p_root);
+
+ } break;
+ case MODE_UNDO: {
+ undo_redo->add_undo_method(p_node, "set_owner", p_root);
+
+ } break;
+ }
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
- _node_replace_owner(p_base, p_node->get_child(i), p_root);
+ _node_replace_owner(p_base, p_node->get_child(i), p_root, p_mode);
}
}
@@ -1904,6 +2007,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT);
if (selection.size() == 1) {
+
+ menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT);
menu->add_separator();
menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE);
menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM);
@@ -2090,6 +2195,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN);
ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"), KEY_MASK_CMD | KEY_D);
ED_SHORTCUT("scene_tree/reparent", TTR("Reparent"));
+ ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root"));
ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene"));
ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene"));
ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_C);
@@ -2153,6 +2259,10 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
remote_tree = NULL;
button_hb->hide();
+ create_root_dialog = memnew(VBoxContainer);
+ vbc->add_child(create_root_dialog);
+ create_root_dialog->hide();
+
scene_tree = memnew(SceneTreeEditor(false, true, true));
vbc->add_child(scene_tree);
@@ -2227,4 +2337,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
add_child(clear_inherit_confirm);
set_process_input(true);
+ set_process(true);
+
+ EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
}
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index ed13e063bb..fd74611bde 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -68,6 +68,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_MOVE_DOWN,
TOOL_DUPLICATE,
TOOL_REPARENT,
+ TOOL_MAKE_ROOT,
TOOL_NEW_SCENE_FROM,
TOOL_MERGE_FROM_SCENE,
TOOL_MULTI_EDIT,
@@ -80,7 +81,12 @@ class SceneTreeDock : public VBoxContainer {
TOOL_SCENE_OPEN,
TOOL_SCENE_CLEAR_INHERITANCE,
TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM,
- TOOL_SCENE_OPEN_INHERITED
+ TOOL_SCENE_OPEN_INHERITED,
+
+ TOOL_CREATE_2D_SCENE,
+ TOOL_CREATE_3D_SCENE,
+ TOOL_CREATE_USER_INTERFACE,
+
};
enum {
@@ -134,13 +140,22 @@ class SceneTreeDock : public VBoxContainer {
Node *edited_scene;
EditorNode *editor;
+ VBoxContainer *create_root_dialog;
+
void _add_children_to_popup(Object *p_obj, int p_depth);
void _node_reparent(NodePath p_path, bool p_keep_global_xform);
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform);
void _set_owners(Node *p_owner, const Array &p_nodes);
- void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root);
+
+ enum ReplaceOwnerMode {
+ MODE_BIDI,
+ MODE_DO,
+ MODE_UNDO
+ };
+
+ void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode = MODE_BIDI);
void _load_request(const String &p_path);
void _script_open_request(const Ref<Script> &p_script);
@@ -215,6 +230,9 @@ public:
void replace_node(Node *p_node, Node *p_by_node);
void open_script_dialog(Node *p_for_node);
+
+ ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; }
+
SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
};
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 7d2127d4f8..88d614ab89 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -166,6 +166,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
}
TreeItem *item = tree->create_item(p_parent);
+
item->set_text(0, p_node->get_name());
if (can_rename && !part_of_subscene /*(p_node->get_owner() == get_scene_node() || p_node==get_scene_node())*/)
item->set_editable(0, true);
@@ -196,7 +197,9 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
if (part_of_subscene) {
//item->set_selectable(0,marked_selectable);
- item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
+ if (valid_types.size() == 0) {
+ item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
+ }
} else if (marked.has(p_node)) {
@@ -323,6 +326,22 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
keep = keep || child_keep;
}
+ if (valid_types.size()) {
+ bool valid = false;
+ for (int i = 0; i < valid_types.size(); i++) {
+ if (p_node->is_class(valid_types[i])) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (!valid) {
+ //item->set_selectable(0,marked_selectable);
+ item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
+ item->set_selectable(0, false);
+ }
+ }
+
if (!keep) {
memdelete(item);
return false;
@@ -716,6 +735,10 @@ bool SceneTreeEditor::get_display_foreign_nodes() const {
return display_foreign;
}
+void SceneTreeEditor::set_valid_types(const Vector<StringName> &p_valid) {
+ valid_types = p_valid;
+}
+
void SceneTreeEditor::set_editor_selection(EditorSelection *p_selection) {
editor_selection = p_selection;
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index b173d7d215..c4f63f5736 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -131,6 +131,8 @@ class SceneTreeEditor : public Control {
List<StringName> *script_types;
bool _is_script_type(const StringName &p_type) const;
+ Vector<StringName> valid_types;
+
public:
void set_filter(const String &p_filter);
String get_filter() const;
@@ -147,6 +149,7 @@ public:
void set_editor_selection(EditorSelection *p_selection);
void set_show_enabled_subscene(bool p_show) { show_enabled_subscene = p_show; }
+ void set_valid_types(const Vector<StringName> &p_valid);
void update_tree() { _update_tree(); }
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 57a003060e..24c4ba4cb7 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -582,6 +582,9 @@ void ScriptCreateDialog::_bind_methods() {
ClassDB::bind_method("_path_changed", &ScriptCreateDialog::_path_changed);
ClassDB::bind_method("_path_entered", &ScriptCreateDialog::_path_entered);
ClassDB::bind_method("_template_changed", &ScriptCreateDialog::_template_changed);
+
+ ClassDB::bind_method(D_METHOD("config", "inherits", "path"), &ScriptCreateDialog::config);
+
ADD_SIGNAL(MethodInfo("script_created", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
}
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index 62848a6035..9ce0e973f7 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -1249,6 +1249,9 @@ void ScriptEditorDebugger::stop() {
EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree();
EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons();
+ Node *node = editor->get_scene_tree_dock()->get_tree_editor()->get_selected();
+ editor->push_item(node);
+
if (hide_on_stop) {
if (is_visible_in_tree())
EditorNode::get_singleton()->hide_bottom_panel();
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index eb9ab93228..fe379703e5 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -54,14 +54,14 @@ void EditorSettingsDialog::_settings_changed() {
void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
- String full_name = property_editor->get_full_item_path(p_name);
+ String full_name = inspector->get_full_item_path(p_name);
// Small usability workaround to update the text color settings when the
// color theme is changed
if (full_name == "text_editor/theme/color_theme") {
- property_editor->get_property_editor()->update_tree();
+ inspector->get_inspector()->update_tree();
} else if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") {
- EditorSettings::get_singleton()->set_manually("interface/theme/preset", 1); // set preset to Custom
+ EditorSettings::get_singleton()->set_manually("interface/theme/preset", "Custom"); // set preset to Custom
} else if (full_name.begins_with("text_editor/highlighting")) {
EditorSettings::get_singleton()->set_manually("text_editor/theme/color_theme", "Custom");
}
@@ -88,8 +88,8 @@ void EditorSettingsDialog::popup_edit_settings() {
EditorSettings::get_singleton()->list_text_editor_themes(); // make sure we have an up to date list of themes
- property_editor->edit(EditorSettings::get_singleton());
- property_editor->get_property_editor()->update_tree();
+ inspector->edit(EditorSettings::get_singleton());
+ inspector->get_inspector()->update_tree();
search_box->select_all();
search_box->grab_focus();
@@ -120,7 +120,7 @@ void EditorSettingsDialog::_clear_search_box() {
return;
search_box->clear();
- property_editor->get_property_editor()->update_tree();
+ inspector->get_inspector()->update_tree();
}
void EditorSettingsDialog::_clear_shortcut_search_box() {
@@ -158,7 +158,7 @@ void EditorSettingsDialog::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
_update_icons();
// Update theme colors.
- property_editor->update_category_list();
+ inspector->update_category_list();
_update_shortcuts();
} break;
}
@@ -202,6 +202,11 @@ void EditorSettingsDialog::_update_icons() {
shortcut_search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
clear_button->set_icon(get_icon("Close", "EditorIcons"));
shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons"));
+
+ restart_close_button->set_icon(get_icon("Close", "EditorIcons"));
+ restart_container->add_style_override("panel", get_stylebox("bg", "Tree"));
+ restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons"));
+ restart_label->add_color_override("font_color", get_color("error_color", "Editor"));
}
void EditorSettingsDialog::_update_shortcuts() {
@@ -388,6 +393,18 @@ void EditorSettingsDialog::_focus_current_search_box() {
}
}
+void EditorSettingsDialog::_editor_restart() {
+ EditorNode::get_singleton()->save_all_scenes_and_restart();
+}
+
+void EditorSettingsDialog::_editor_restart_request() {
+ restart_container->show();
+}
+
+void EditorSettingsDialog::_editor_restart_close() {
+ restart_container->hide();
+}
+
void EditorSettingsDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorSettingsDialog::_unhandled_input);
@@ -402,6 +419,10 @@ void EditorSettingsDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_press_a_key_confirm"), &EditorSettingsDialog::_press_a_key_confirm);
ClassDB::bind_method(D_METHOD("_wait_for_key"), &EditorSettingsDialog::_wait_for_key);
ClassDB::bind_method(D_METHOD("_tabs_tab_changed"), &EditorSettingsDialog::_tabs_tab_changed);
+
+ ClassDB::bind_method(D_METHOD("_editor_restart_request"), &EditorSettingsDialog::_editor_restart_request);
+ ClassDB::bind_method(D_METHOD("_editor_restart"), &EditorSettingsDialog::_editor_restart);
+ ClassDB::bind_method(D_METHOD("_editor_restart_close"), &EditorSettingsDialog::_editor_restart_close);
}
EditorSettingsDialog::EditorSettingsDialog() {
@@ -434,14 +455,35 @@ EditorSettingsDialog::EditorSettingsDialog() {
hbc->add_child(clear_button);
clear_button->connect("pressed", this, "_clear_search_box");
- property_editor = memnew(SectionedPropertyEditor);
- //property_editor->hide_top_label();
- property_editor->get_property_editor()->set_use_filter(true);
- property_editor->register_search_box(search_box);
- property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- property_editor->get_property_editor()->set_undo_redo(undo_redo);
- tab_general->add_child(property_editor);
- property_editor->get_property_editor()->connect("property_edited", this, "_settings_property_edited");
+ inspector = memnew(SectionedInspector);
+ //inspector->hide_top_label();
+ inspector->get_inspector()->set_use_filter(true);
+ inspector->register_search_box(search_box);
+ inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector->get_inspector()->set_undo_redo(undo_redo);
+ tab_general->add_child(inspector);
+ inspector->get_inspector()->connect("property_edited", this, "_settings_property_edited");
+ inspector->get_inspector()->connect("restart_requested", this, "_editor_restart_request");
+
+ restart_container = memnew(PanelContainer);
+ tab_general->add_child(restart_container);
+ HBoxContainer *restart_hb = memnew(HBoxContainer);
+ restart_container->add_child(restart_hb);
+ restart_icon = memnew(TextureRect);
+ restart_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ restart_hb->add_child(restart_icon);
+ restart_label = memnew(Label);
+ restart_label->set_text(TTR("Editor must be restarted for changes to take effect"));
+ restart_hb->add_child(restart_label);
+ restart_hb->add_spacer();
+ Button *restart_button = memnew(Button);
+ restart_button->connect("pressed", this, "_editor_restart");
+ restart_hb->add_child(restart_button);
+ restart_button->set_text(TTR("Save & Restart"));
+ restart_close_button = memnew(ToolButton);
+ restart_close_button->connect("pressed", this, "_editor_restart_close");
+ restart_hb->add_child(restart_close_button);
+ restart_container->hide();
// Shortcuts Tab
diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h
index 6676e870d0..6cf2eb6bdf 100644
--- a/editor/settings_config_dialog.h
+++ b/editor/settings_config_dialog.h
@@ -31,9 +31,14 @@
#ifndef SETTINGS_CONFIG_DIALOG_H
#define SETTINGS_CONFIG_DIALOG_H
-#include "property_editor.h"
+#include "editor/editor_sectioned_inspector.h"
+#include "editor_inspector.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/tab_container.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/gui/tool_button.h"
class EditorSettingsDialog : public AcceptDialog {
@@ -49,7 +54,7 @@ class EditorSettingsDialog : public AcceptDialog {
LineEdit *shortcut_search_box;
ToolButton *clear_button;
ToolButton *shortcut_clear_button;
- SectionedPropertyEditor *property_editor;
+ SectionedInspector *inspector;
Timer *timer;
@@ -89,6 +94,15 @@ class EditorSettingsDialog : public AcceptDialog {
static void _undo_redo_callback(void *p_self, const String &p_name);
+ Label *restart_label;
+ TextureRect *restart_icon;
+ PanelContainer *restart_container;
+ ToolButton *restart_close_button;
+
+ void _editor_restart_request();
+ void _editor_restart();
+ void _editor_restart_close();
+
protected:
static void _bind_methods();
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 873420b383..35544f711b 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -33,9 +33,11 @@
#include "geometry.h"
#include "quick_hull.h"
#include "scene/3d/camera.h"
+#include "scene/3d/soft_body.h"
#include "scene/resources/box_shape.h"
#include "scene/resources/capsule_shape.h"
#include "scene/resources/convex_polygon_shape.h"
+#include "scene/resources/cylinder_shape.h"
#include "scene/resources/plane_shape.h"
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/ray_shape.h"
@@ -202,7 +204,7 @@ void EditorSpatialGizmo::add_unscaled_billboard(const Ref<Material> &p_material,
}
selectable_icon_size = p_scale;
- mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 40.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 80.0f));
+ mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f));
ins.mesh = mesh;
ins.unscaled = true;
@@ -212,7 +214,7 @@ void EditorSpatialGizmo::add_unscaled_billboard(const Ref<Material> &p_material,
VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());
}
- selectable_icon_size = p_scale * 2.0;
+ selectable_icon_size = p_scale;
instances.push_back(ins);
}
@@ -255,8 +257,12 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi
for (int i = 0; i < p_handles.size(); i++) {
Color col(1, 1, 1, 1);
+ if (is_gizmo_handle_highlighted(i))
+ col = Color(0, 0, 1, 0.9);
+
if (SpatialEditor::get_singleton()->get_over_gizmo_handle() != i)
- col = Color(0.9, 0.9, 0.9, 0.9);
+ col.a = 0.8;
+
w[i] = col;
}
}
@@ -475,8 +481,9 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
float scale = t.origin.distance_to(p_camera->get_camera_transform().origin);
if (p_camera->get_projection() == Camera::PROJECTION_ORTHOGONAL) {
- float h = Math::abs(p_camera->get_size());
- scale = (h * 2.0);
+ float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect();
+ float size = p_camera->get_size();
+ scale = size / aspect;
}
Point2 center = p_camera->unproject_position(t.origin);
@@ -1912,6 +1919,100 @@ VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel *p_car_wheel) {
///////////
+void SoftBodySpatialGizmo::redraw() {
+ clear();
+
+ if (!soft_body || soft_body->get_mesh().is_null()) {
+ return;
+ }
+
+ // find mesh
+
+ Vector<Vector3> lines;
+
+ soft_body->get_mesh()->generate_debug_mesh_lines(lines);
+
+ if (!lines.size()) {
+ return;
+ }
+
+ Vector<Vector3> points;
+ soft_body->get_mesh()->generate_debug_mesh_indices(points);
+
+ soft_body->get_mesh()->clear_cache();
+
+ Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape");
+ Ref<Material> material = create_material("shape_material", gizmo_color);
+
+ add_lines(lines, material);
+ add_collision_segments(lines);
+ add_handles(points);
+}
+
+bool SoftBodySpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
+ return EditorSpatialGizmo::intersect_ray(p_camera, p_point, r_pos, r_normal, r_gizmo_handle, p_sec_first);
+
+ /* Perform a shape cast but doesn't work with softbody
+ PhysicsDirectSpaceState *space_state = PhysicsServer::get_singleton()->space_get_direct_state(SceneTree::get_singleton()->get_root()->get_world()->get_space());
+ if (!physics_sphere_shape.is_valid()) {
+ physics_sphere_shape = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_SPHERE);
+ real_t radius = 0.02;
+ PhysicsServer::get_singleton()->shape_set_data(physics_sphere_shape, radius);
+ }
+
+ Vector3 sphere_motion(p_camera->project_ray_normal(p_point));
+ real_t closest_safe;
+ real_t closest_unsafe;
+ PhysicsDirectSpaceState::ShapeRestInfo result;
+ bool collided = space_state->cast_motion(
+ physics_sphere_shape,
+ p_camera->get_transform(),
+ sphere_motion * Vector3(1000, 1000, 1000),
+ 0.f,
+ closest_safe,
+ closest_unsafe,
+ Set<RID>(),
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ &result);
+
+ if (collided) {
+
+ if (result.collider_id == soft_body->get_instance_id()) {
+ print_line("Collided");
+ } else {
+ print_line("Collided but with wrong object: " + itos(result.collider_id));
+ }
+ } else {
+ print_line("Not collided, motion: x: " + rtos(sphere_motion[0]) + " y: " + rtos(sphere_motion[1]) + " z: " + rtos(sphere_motion[2]));
+ }
+ return false;
+ */
+}
+
+void SoftBodySpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+ soft_body->pin_point_toggle(p_idx);
+ redraw();
+}
+
+bool SoftBodySpatialGizmo::is_gizmo_handle_highlighted(int idx) const {
+ return soft_body->is_point_pinned(idx);
+}
+
+SoftBodySpatialGizmo::SoftBodySpatialGizmo(SoftBody *p_soft_physics_body) :
+ EditorSpatialGizmo(),
+ soft_body(p_soft_physics_body) {
+ set_spatial_node(p_soft_physics_body);
+}
+
+SoftBodySpatialGizmo::~SoftBodySpatialGizmo() {
+ //if (!physics_sphere_shape.is_valid()) {
+ // PhysicsServer::get_singleton()->free(physics_sphere_shape);
+ //}
+}
+
+///////////
+
String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
Ref<Shape> s = cs->get_shape();
@@ -1933,6 +2034,11 @@ String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
return p_idx == 0 ? "Radius" : "Height";
}
+ if (Object::cast_to<CylinderShape>(*s)) {
+
+ return p_idx == 0 ? "Radius" : "Height";
+ }
+
if (Object::cast_to<RayShape>(*s)) {
return "Length";
@@ -1964,6 +2070,12 @@ Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const {
return p_idx == 0 ? cs->get_radius() : cs->get_height();
}
+ if (Object::cast_to<CylinderShape>(*s)) {
+
+ Ref<CylinderShape> cs = s;
+ return p_idx == 0 ? cs->get_radius() : cs->get_height();
+ }
+
if (Object::cast_to<RayShape>(*s)) {
Ref<RayShape> cs = s;
@@ -2044,6 +2156,24 @@ void CollisionShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const P
else if (p_idx == 1)
cs->set_height(d * 2.0);
}
+
+ if (Object::cast_to<CylinderShape>(*s)) {
+
+ Vector3 axis;
+ axis[p_idx == 0 ? 0 : 1] = 1.0;
+ Ref<CylinderShape> cs = s;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ cs->set_radius(d);
+ else if (p_idx == 1)
+ cs->set_height(d * 2.0);
+ }
}
void CollisionShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
Ref<Shape> s = cs->get_shape();
@@ -2105,6 +2235,31 @@ void CollisionShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_resto
ur->commit_action();
}
+ if (Object::cast_to<CylinderShape>(*s)) {
+
+ Ref<CylinderShape> ss = s;
+ if (p_cancel) {
+ if (p_idx == 0)
+ ss->set_radius(p_restore);
+ else
+ ss->set_height(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 0) {
+ ur->create_action(TTR("Change Cylinder Shape Radius"));
+ ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
+ ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
+ } else {
+ ur->create_action(TTR("Change Cylinder Shape Height"));
+ ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
+ ur->add_undo_method(ss.ptr(), "set_height", p_restore);
+ }
+
+ ur->commit_action();
+ }
+
if (Object::cast_to<RayShape>(*s)) {
Ref<RayShape> ss = s;
@@ -2281,6 +2436,67 @@ void CollisionShapeSpatialGizmo::redraw() {
add_handles(handles);
}
+ if (Object::cast_to<CylinderShape>(*s)) {
+
+ Ref<CylinderShape> cs = s;
+ float radius = cs->get_radius();
+ float height = cs->get_height();
+
+ Vector<Vector3> points;
+
+ Vector3 d(0, height * 0.5, 0);
+ for (int i = 0; i < 360; i++) {
+
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(b.x, 0, b.y) + d);
+
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ points.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 90 == 0) {
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ add_lines(points, material);
+
+ Vector<Vector3> collision_segments;
+
+ for (int i = 0; i < 64; i++) {
+
+ float ra = i * Math_PI * 2.0 / 64.0;
+ float rb = (i + 1) * Math_PI * 2.0 / 64.0;
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
+ collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
+ collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 16 == 0) {
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
+ collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ add_collision_segments(collision_segments);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(cs->get_radius(), 0, 0));
+ handles.push_back(Vector3(0, cs->get_height() * 0.5, 0));
+ add_handles(handles);
+ }
+
if (Object::cast_to<PlaneShape>(*s)) {
Ref<PlaneShape> ps = s;
@@ -3155,10 +3371,10 @@ NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p
navmesh = p_navmesh;
}
- //////
- ///
- ///
- ///
+//////
+///
+///
+///
#define BODY_A_RADIUS 0.25
#define BODY_B_RADIUS 0.27
@@ -3934,6 +4150,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
return lsg;
}
+ if (Object::cast_to<SoftBody>(p_spatial)) {
+
+ Ref<SoftBodySpatialGizmo> misg = memnew(SoftBodySpatialGizmo(Object::cast_to<SoftBody>(p_spatial)));
+ return misg;
+ }
+
if (Object::cast_to<MeshInstance>(p_spatial)) {
Ref<MeshInstanceSpatialGizmo> misg = memnew(MeshInstanceSpatialGizmo(Object::cast_to<MeshInstance>(p_spatial)));
@@ -3964,6 +4186,7 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
return misg;
}
*/
+
if (Object::cast_to<CollisionShape>(p_spatial)) {
Ref<CollisionShapeSpatialGizmo> misg = memnew(CollisionShapeSpatialGizmo(Object::cast_to<CollisionShape>(p_spatial)));
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 924f82dc16..198d028516 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -331,6 +331,23 @@ public:
BakedIndirectLightGizmo(BakedLightmap *p_baker = NULL);
};
+class SoftBodySpatialGizmo : public EditorSpatialGizmo {
+ GDCLASS(SoftBodySpatialGizmo, EditorSpatialGizmo);
+
+ class SoftBody *soft_body;
+ //RID physics_sphere_shape; // Used for raycast that doesn't work, in this moment, with softbody
+
+public:
+ void redraw();
+ virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel);
+
+ virtual bool is_gizmo_handle_highlighted(int idx) const;
+
+ SoftBodySpatialGizmo(SoftBody *p_soft_physics_body = NULL);
+ ~SoftBodySpatialGizmo();
+};
+
class CollisionShapeSpatialGizmo : public EditorSpatialGizmo {
GDCLASS(CollisionShapeSpatialGizmo, EditorSpatialGizmo);
diff --git a/editor/translations/ar.po b/editor/translations/ar.po
index cd193c4fc4..ccf2b97d9a 100644
--- a/editor/translations/ar.po
+++ b/editor/translations/ar.po
@@ -8078,6 +8078,9 @@ msgstr ""
msgid "Invalid font size."
msgstr ""
+#~ msgid "Next"
+#~ msgstr "التالي"
+
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "لا يمكن أن يحتوي علي '/' أو ':'"
@@ -8090,9 +8093,6 @@ msgstr ""
#~ msgid "Can't write file."
#~ msgstr "لا يمكن كتابة الملÙ."
-#~ msgid "Next"
-#~ msgstr "التالي"
-
#~ msgid "Not found!"
#~ msgstr "لم يوجد!"
diff --git a/editor/translations/bn.po b/editor/translations/bn.po
index 2704d509eb..3d00e3450c 100644
--- a/editor/translations/bn.po
+++ b/editor/translations/bn.po
@@ -8547,6 +8547,13 @@ msgstr "ফনà§à¦Ÿ তà§à¦²à¦¤à§‡/লোডে সমসà§à¦¯à¦¾ হয়েà¦
msgid "Invalid font size."
msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "পূরà§à¦¬à§‡à¦° টà§à¦¯à¦¾à¦¬"
+
+#~ msgid "Next"
+#~ msgstr "পরবরà§à¦¤à§€"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "অকারà§à¦¯à¦•র অà§à¦¯à¦¾à¦•শন ('/' বা ':' ছাড়া কিছà§à¦‡ যাবে না)।"
@@ -8576,9 +8583,6 @@ msgstr "ফনà§à¦Ÿà§‡à¦° আকার অগà§à¦°à¦¹à¦¨à¦¯à§‹à¦—à§à¦¯à¥¤"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "পà§à¦°à¦•লà§à¦ªà§‡à¦° পথে engine.cfg তৈরি করা সমà§à¦­à¦¬ হয়নি।"
-#~ msgid "Next"
-#~ msgstr "পরবরà§à¦¤à§€"
-
#~ msgid "Not found!"
#~ msgstr "খà§à¦à¦œà§‡ পাওয়া যায়নি!"
diff --git a/editor/translations/ca.po b/editor/translations/ca.po
index 08d842a3c3..d2bffb0f84 100644
--- a/editor/translations/ca.po
+++ b/editor/translations/ca.po
@@ -2,23 +2,21 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# BennyBeat <bennybeat@gmail.com>, 2017.
# Javier Ocampos <xavier.ocampos@gmail.com>, 2018.
# Roger Blanco Ribera <roger.blancoribera@gmail.com>, 2016-2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-05-17 23:48+0000\n"
-"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n"
+"PO-Revision-Date: 2018-06-08 03:41+0000\n"
+"Last-Translator: Roger Blanco Ribera <roger.blancoribera@gmail.com>\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/godot-engine/"
"godot/ca/>\n"
"Language: ca\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5693,9 +5691,8 @@ msgid "Options"
msgstr "Opcions"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Tens,Moltes,Diverses,Opcions!"
+msgstr "Té,Moltes,Opcions"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5981,9 +5978,8 @@ msgid "Imported Project"
msgstr "Project importat"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nom del Projecte:"
+msgstr "El nom del Projecte no és vàlid."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6184,13 +6180,12 @@ msgid "Mouse Button"
msgstr "Botó del ratolí"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
-"Nom d'acció no vàlid. no pot estar buit ni contenir '/', ':', '=', '\\' o "
-"'\"'"
+"Nom d'acció no vàlid. No pot estar buit ni contenir '/', ':', '=', '\\' o "
+"'\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8218,6 +8213,13 @@ msgstr "Error carregant lletra."
msgid "Invalid font size."
msgstr "La mida de la lletra no és vàlida."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Pestanya Anterior"
+
+#~ msgid "Next"
+#~ msgstr "Següent"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "L'Acció no és vàlida (no es pot utilitzar ' / ' o ':')."
@@ -8245,9 +8247,6 @@ msgstr "La mida de la lletra no és vàlida."
#~ msgstr ""
#~ "No es pot trobat el el fitxer 'project.godot' en el camí del projecte."
-#~ msgid "Next"
-#~ msgstr "Següent"
-
#~ msgid "Not found!"
#~ msgstr "No s'ha trobat!"
diff --git a/editor/translations/cs.po b/editor/translations/cs.po
index 6f46ba7535..1066bbad94 100644
--- a/editor/translations/cs.po
+++ b/editor/translations/cs.po
@@ -8124,6 +8124,13 @@ msgstr "Chyba nahrávání fontu."
msgid "Invalid font size."
msgstr "Neplatná velikost fontu."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Předchozí záložka"
+
+#~ msgid "Next"
+#~ msgstr "Další"
+
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "Nesmí obsaovat '/' nebo ':'"
@@ -8137,9 +8144,6 @@ msgstr "Neplatná velikost fontu."
#~ msgid "Can't write file."
#~ msgstr "Nelze zapsat soubor."
-#~ msgid "Next"
-#~ msgstr "Další"
-
#~ msgid "Not found!"
#~ msgstr "Nenalezeno!"
diff --git a/editor/translations/da.po b/editor/translations/da.po
index 3b3f4b3e54..3b5854334a 100644
--- a/editor/translations/da.po
+++ b/editor/translations/da.po
@@ -8192,6 +8192,13 @@ msgstr "Error loading skrifttype."
msgid "Invalid font size."
msgstr "Ugyldig skriftstørrelse."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Forrige fane"
+
+#~ msgid "Next"
+#~ msgstr "Næste"
+
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "Kan ikke indeholde '/' eller ':'"
@@ -8205,9 +8212,6 @@ msgstr "Ugyldig skriftstørrelse."
#~ msgid "Can't write file."
#~ msgstr "Kan ikke skrive til fil."
-#~ msgid "Next"
-#~ msgstr "Næste"
-
#~ msgid "Not found!"
#~ msgstr "Ikke fundet!"
diff --git a/editor/translations/de.po b/editor/translations/de.po
index c09b11bda1..d5d63f654b 100644
--- a/editor/translations/de.po
+++ b/editor/translations/de.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Alexander Mahr <alex.mahr@gmail.com>, 2016.
# Andreas Esau <andreasesau@gmail.com>, 2016.
# Andreas Haas <liu.gam3@gmail.com>, 2016.
@@ -18,6 +17,7 @@
# Kim <github@aggsol.de>, 2017.
# Metin Celik <metincelik88@gmail.com>, 2018.
# Neicul <neicul@gmx.de>, 2018.
+# nimradium <nimra242001@gmail.com>, 2018.
# Oliver Ruehl <oliver@ruehldesign.co>, 2016-2017.
# Paul-Vincent Roll <paviro@me.com>, 2016.
# Peter Friedland <peter_friedland@gmx.de>, 2016.
@@ -27,13 +27,12 @@
# Tim Schellenberg <smwleod@gmail.com>, 2017.
# Timo Schwarzer <account@timoschwarzer.com>, 2016-2018.
# viernullvier <hannes.breul+github@gmail.com>, 2016.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-05-24 15:37+0000\n"
-"Last-Translator: Metin Celik <metincelik88@gmail.com>\n"
+"PO-Revision-Date: 2018-06-19 19:38+0000\n"
+"Last-Translator: nimradium <nimra242001@gmail.com>\n"
"Language-Team: German <https://hosted.weblate.org/projects/godot-engine/"
"godot/de/>\n"
"Language: de\n"
@@ -41,7 +40,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -332,7 +331,8 @@ msgstr "Optimieren"
#: editor/animation_editor.cpp
msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
msgstr ""
-"AnimationPlayer aus dem Szenenbaum auswählen um Animationen zu bearbeiten."
+"Wählen Sie einen AnimationPlayer aus dem Szenenbaum aus, um Animationen zu "
+"bearbeiten."
#: editor/animation_editor.cpp
msgid "Key"
@@ -2483,7 +2483,7 @@ msgstr "Mirrors werden geladen, bitte warten..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
-msgstr "Template-Version ‚%s‘ entfernen?"
+msgstr "Template-Version '%s' entfernen?"
#: editor/export_template_manager.cpp
msgid "Can't open export templates zip."
@@ -4047,7 +4047,7 @@ msgstr "Mesh hat keine Oberfläche von der Umrisse erzeugt werden könnten!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr "Mesh primitive type ist nicht PRIMITIVE_TRIANGLES!"
+msgstr "Der Mesh-Grundtyp ist nicht ist nicht PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -5737,9 +5737,8 @@ msgid "Options"
msgstr "Optionen"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Enthalten,Viele,Einige,Optionen!"
+msgstr "Einstellungen"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6024,9 +6023,8 @@ msgid "Imported Project"
msgstr "Importiertes Projekt"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Projektname:"
+msgstr "Ungültiger Projektname."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6230,13 +6228,12 @@ msgid "Mouse Button"
msgstr "Maustaste"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
-"Ungültiger Aktionsname. Er kann weder leer sein, noch kann er '/', ':', '=', "
-"'\\' oder '\"' enthalten"
+"Ungültiger Aktionsname. Er kann weder leer sein noch ‚/‘, ‚:‘, ‚=‘, ‘\\‘ "
+"oder ‚\"‘ enthalten."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8287,6 +8284,13 @@ msgstr "Fehler beim Laden der Schriftart."
msgid "Invalid font size."
msgstr "Ungültige Schriftgröße."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Vorheriger Tab"
+
+#~ msgid "Next"
+#~ msgstr "Nächste"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr ""
#~ "Ungültiger Name für Aktion (alle Zeichen außer ‚/‘ und ‚:‘ möglich)."
@@ -8313,9 +8317,6 @@ msgstr "Ungültige Schriftgröße."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "project.godot konnte nicht im Projektpfad gefunden werden."
-#~ msgid "Next"
-#~ msgstr "Nächste"
-
#~ msgid "Not found!"
#~ msgstr "Nicht gefunden!"
diff --git a/editor/translations/el.po b/editor/translations/el.po
index ad2eb41c4d..b3275b4647 100644
--- a/editor/translations/el.po
+++ b/editor/translations/el.po
@@ -8260,6 +8260,13 @@ msgstr "Σφάλμα κατά την φόÏτωση της γÏαμματοσεÎ
msgid "Invalid font size."
msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ΠÏοηγοÏμενη καÏτέλα"
+
+#~ msgid "Next"
+#~ msgstr "Επόμενο"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Μη έγκυÏη ενέÏγεια (Όλα επιτÏέποντα εκτός από το '/' και το ':')."
@@ -8287,9 +8294,6 @@ msgstr "Μη έγκυÏο μέγεθος γÏαμματοσειÏάς."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Δεν βÏέθηκε το project.godot στη διαδÏομή του έÏγου."
-#~ msgid "Next"
-#~ msgstr "Επόμενο"
-
#~ msgid "Not found!"
#~ msgstr "Δεν βÏέθηκε!"
diff --git a/editor/translations/es.po b/editor/translations/es.po
index 405130c465..89118d2501 100644
--- a/editor/translations/es.po
+++ b/editor/translations/es.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Addiel Lucena Perez <addiell2017@gmail.com>, 2017.
# Aleix Sanchis <aleixsanchis@hotmail.com>, 2017, 2018.
# Alejandro Alvarez <eliluminado00@gmail.com>, 2017.
@@ -23,19 +22,19 @@
# Lonsfor <lotharw@protonmail.com>, 2017-2018.
# Mario Nachbaur <manachbaur@gmail.com>, 2018.
# Oscar Carballal <oscar.carballal@protonmail.com>, 2017-2018.
+# R. Joshua Seville <rjoshua@protonmail.com>, 2018.
# Rabid Orange <theorangerabid@gmail.com>, 2017, 2018.
# Roger Blanco Ribera <roger.blancoribera@gmail.com>, 2016-2018.
# Sebastian Silva <sebastian@fuentelibre.org>, 2016.
# Swyter <swyterzone@gmail.com>, 2016-2017.
# Vazquinhos <vazquinhos@gmail.com>, 2018.
# Yovani Damián <blackblex@gmail.com>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-06-01 00:44+0000\n"
-"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n"
+"PO-Revision-Date: 2018-06-22 08:31+0000\n"
+"Last-Translator: R. Joshua Seville <rjoshua@protonmail.com>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/"
"godot/es/>\n"
"Language: es\n"
@@ -43,7 +42,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -269,7 +268,7 @@ msgstr "Zoom de Animación."
#: editor/animation_editor.cpp
msgid "Length (s):"
-msgstr "Duración (seg):"
+msgstr "Duración (segs.):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
@@ -548,7 +547,7 @@ msgstr "Cambiar"
#: editor/create_dialog.cpp
msgid "Create New %s"
-msgstr "Crear nuevo %s"
+msgstr "Crear Nuevo %s"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -738,7 +737,7 @@ msgstr "Contribuidores de Godot"
#: editor/editor_about.cpp
msgid "Project Founders"
-msgstr "Fundadores del proyecto"
+msgstr "Fundadores del Proyecto"
#: editor/editor_about.cpp
msgid "Lead Developer"
@@ -1978,7 +1977,7 @@ msgstr "Proyecto"
#: editor/editor_node.cpp
msgid "Project Settings"
-msgstr "Ajustes del proyecto"
+msgstr "Ajustes del Proyecto"
#: editor/editor_node.cpp
msgid "Run Script"
@@ -2089,7 +2088,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Editor"
-msgstr "El editor"
+msgstr "Editor"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
@@ -2144,7 +2143,7 @@ msgstr "Acerca de"
#: editor/editor_node.cpp
msgid "Play the project."
-msgstr "Inicia el proyecto para poder jugarlo."
+msgstr "Reproducir el proyecto."
#: editor/editor_node.cpp
msgid "Play"
@@ -3496,7 +3495,7 @@ msgstr ""
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Bake Lightmaps"
-msgstr "Calculando «lightmaps»"
+msgstr "Calculando Lightmaps"
#: editor/plugins/camera_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3519,7 +3518,7 @@ msgstr "Paso de Cuadrícula:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Offset:"
-msgstr "Desplazamiento de rotación:"
+msgstr "Desplazamiento de Rotación:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Step:"
@@ -4593,7 +4592,7 @@ msgstr "¡El portapapeles de recursos está vacío!"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
msgid "Open in Editor"
-msgstr "Abrir en el editor"
+msgstr "Abrir en el Editor"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_editor.cpp
@@ -5341,7 +5340,7 @@ msgstr "Modo escalado (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
-msgstr "Coordenadas Locales"
+msgstr "Local Coords (Coordenadas Locales)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
@@ -5741,9 +5740,8 @@ msgid "Options"
msgstr "Opciones"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "¡Tienes,Muchas,Y,Variadas,Opciones!"
+msgstr "Tienes, Muchas, Opciones"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6027,12 +6025,11 @@ msgstr "Por favor elija una carpeta vacía."
#: editor/project_manager.cpp
msgid "Imported Project"
-msgstr "Proyecto importado"
+msgstr "Proyecto Importado"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nombre del proyecto:"
+msgstr "Nombre de Proyecto Inválido."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6076,11 +6073,11 @@ msgstr "Renombrar proyecto"
#: editor/project_manager.cpp
msgid "New Game Project"
-msgstr "Nuevo proyecto de juego"
+msgstr "Nuevo Proyecto de Juego"
#: editor/project_manager.cpp
msgid "Import Existing Project"
-msgstr "Importar proyecto existente"
+msgstr "Importar Proyecto Existente"
#: editor/project_manager.cpp
msgid "Import & Edit"
@@ -6088,7 +6085,7 @@ msgstr "Importar y editar"
#: editor/project_manager.cpp
msgid "Create New Project"
-msgstr "Crear proyecto nuevo"
+msgstr "Crear Nuevo Proyecto"
#: editor/project_manager.cpp
msgid "Create & Edit"
@@ -6096,7 +6093,7 @@ msgstr "Crear y editar"
#: editor/project_manager.cpp
msgid "Install Project:"
-msgstr "Instalar proyecto:"
+msgstr "Instalar Proyecto:"
#: editor/project_manager.cpp
msgid "Install & Edit"
@@ -6104,7 +6101,7 @@ msgstr "Instalar y editar"
#: editor/project_manager.cpp
msgid "Project Name:"
-msgstr "Nombre del proyecto:"
+msgstr "Nombre del Proyecto:"
#: editor/project_manager.cpp
msgid "Create folder"
@@ -6112,7 +6109,7 @@ msgstr "Crear carpeta"
#: editor/project_manager.cpp
msgid "Project Path:"
-msgstr "Ruta del proyecto:"
+msgstr "Ruta del Proyecto:"
#: editor/project_manager.cpp
msgid "Browse"
@@ -6120,7 +6117,7 @@ msgstr "Examinar"
#: editor/project_manager.cpp
msgid "Unnamed Project"
-msgstr "Proyecto sin nombre"
+msgstr "Proyecto sin Nombre"
#: editor/project_manager.cpp
msgid "Can't open project"
@@ -6137,8 +6134,8 @@ msgid ""
"the \"Application\" category."
msgstr ""
"No hay una escena principal definida para ejecutar el proyecto.\n"
-"Por favor elija la escena principal en \"Ajustes del proyecto\" en la "
-"categoría \"Aplicación\"."
+"Por favor elija la escena principal en \"Ajustes del Proyecto\" en la "
+"categoría \"Application\"."
#: editor/project_manager.cpp
msgid ""
@@ -6155,8 +6152,8 @@ msgstr "¿Seguro que quieres ejecutar más de un proyecto?"
#: editor/project_manager.cpp
msgid "Remove project from the list? (Folder contents will not be modified)"
msgstr ""
-"¿Quieres quitar proyecto de la lista? (El contenido de la carpeta no se "
-"modificarán)"
+"¿Quieres quitar el proyecto de la lista? (El contenido de la carpeta no se "
+"modificará)"
#: editor/project_manager.cpp
msgid ""
@@ -6193,7 +6190,7 @@ msgstr "Selecciona la carpeta a analizar"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr "Proyecto nuevo"
+msgstr "Nuevo Proyecto"
#: editor/project_manager.cpp
msgid "Templates"
@@ -6237,13 +6234,12 @@ msgid "Mouse Button"
msgstr "Botón del ratón"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
-"Nombre de acción inválido. No puede estar vacío o contener '/', ':', '=', "
-"'\\' or '\"'"
+"Nombre de acción inválido. No puede estar vacío ni contener '/', ':', '=', "
+"'\\' o '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6832,7 +6828,7 @@ msgid ""
"Instance a scene file as a Node. Creates an inherited scene if no root node "
"exists."
msgstr ""
-"Instanciar un archivo de escena como Nodo. Crear una escena heredada si no "
+"Instanciar un archivo de escena como Nodo. Crea una escena heredada si no "
"existe ningún nodo raíz."
#: editor/scene_tree_dock.cpp
@@ -8253,8 +8249,8 @@ msgid ""
"Default Environment as specified in Project Settings (Rendering -> "
"Environment -> Default Environment) could not be loaded."
msgstr ""
-"El entorno especificado por defecto en los Ajustes del Proyecto (Renderizado "
-"-> Ventana -> Entorno por Defecto) no se ha podido cargar."
+"El Entorno por Defecto como se especifica en los Ajustes del Proyecto "
+"(Rendering -> Environment -> Default Environment) no se ha podido cargar."
#: scene/main/viewport.cpp
msgid ""
@@ -8284,6 +8280,13 @@ msgstr "Error al cargar la tipografía."
msgid "Invalid font size."
msgstr "Tamaño de tipografía incorrecto."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Pestaña anterior"
+
+#~ msgid "Next"
+#~ msgstr "Siguiente"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "La acción no es correcta (no puedes utilizar «/» o «:»)."
@@ -8310,9 +8313,6 @@ msgstr "Tamaño de tipografía incorrecto."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "No se encontró project.godot en la ruta del proyecto."
-#~ msgid "Next"
-#~ msgstr "Siguiente"
-
#~ msgid "Not found!"
#~ msgstr "¡No se ha encontrado!"
diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po
index 3514b6b2d9..64ee2404f1 100644
--- a/editor/translations/es_AR.po
+++ b/editor/translations/es_AR.po
@@ -2,17 +2,15 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Diego López <diegodario21@gmail.com>, 2017.
# Lisandro Lorea <lisandrolorea@gmail.com>, 2016-2018.
# Roger Blanco Ribera <roger.blancoribera@gmail.com>, 2016-2018.
# Sebastian Silva <sebastian@sugarlabs.org>, 2016.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-05-03 14:00+0000\n"
+"PO-Revision-Date: 2018-06-06 13:28+0000\n"
"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n"
"Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/"
"godot-engine/godot/es_AR/>\n"
@@ -21,7 +19,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5709,9 +5707,8 @@ msgid "Options"
msgstr "Opciones"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Tienes, Muchas, Variadas, Opciones!"
+msgstr "Tiene,Muchas,Opciones"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6000,9 +5997,8 @@ msgid "Imported Project"
msgstr "Proyecto Importado"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nombre del Proyecto:"
+msgstr "Nombre de Proyecto Inválido."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6207,13 +6203,12 @@ msgid "Mouse Button"
msgstr "Botón de Mouse"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Nombre de acción inválido. No puede estar vacío o contener '/', ':', '=', "
-"'\\' or '\"'"
+"'\\' o '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8244,6 +8239,13 @@ msgstr "Error cargando tipografía."
msgid "Invalid font size."
msgstr "Tamaño de tipografía inválido."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Pestaña anterior"
+
+#~ msgid "Next"
+#~ msgstr "Siguiente"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Acción Invalida (cualquier cosa va menos '/' o ':')."
@@ -8270,9 +8272,6 @@ msgstr "Tamaño de tipografía inválido."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "No se pudo obtener project.godot en la ruta de proyecto."
-#~ msgid "Next"
-#~ msgstr "Siguiente"
-
#~ msgid "Not found!"
#~ msgstr "No se encontró!"
diff --git a/editor/translations/fa.po b/editor/translations/fa.po
index be57fa2fca..f674ef99cc 100644
--- a/editor/translations/fa.po
+++ b/editor/translations/fa.po
@@ -8185,15 +8185,19 @@ msgstr "خطای بارگذاری قلم."
msgid "Invalid font size."
msgstr "اندازهٔ قلم نامعتبر."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "زبانه قبلی"
+
+#~ msgid "Next"
+#~ msgstr "بعدی"
+
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "نمی‌تواند شامل '/' یا ':' باشد"
#~ msgid "Can't write file."
#~ msgstr "ناتوان در نوشتن پرونده."
-#~ msgid "Next"
-#~ msgstr "بعدی"
-
#~ msgid "Not found!"
#~ msgstr "چیزی ÛŒØ§ÙØª نشد!"
diff --git a/editor/translations/fi.po b/editor/translations/fi.po
index 091effca5d..f80efffd42 100644
--- a/editor/translations/fi.po
+++ b/editor/translations/fi.po
@@ -2,19 +2,17 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# basse <basse@roiske.org>, 2017.
-# Bastian Salmela <bastian.salmela@gmail.com>, 2017.
+# Bastian Salmela <bastian.salmela@gmail.com>, 2017, 2018.
# ekeimaja <ekeimaja@gmail.com>, 2017-2018.
# Jarmo Riikonen <amatrelan@gmail.com>, 2017.
# Nuutti Varvikko <nvarvikko@gmail.com>, 2018.
# Sami Lehtilä <sami.lehtila@gmail.com>, 2018.
# Tapani Niemi <tapani.niemi@kapsi.fi>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-06-05 19:27+0000\n"
+"PO-Revision-Date: 2018-06-14 20:37+0000\n"
"Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n"
"Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/"
"godot/fi/>\n"
@@ -22,7 +20,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -38,11 +36,11 @@ msgstr "Animaatio: muuta avainruudun aikaa"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Vaihda animaation siirtymää"
+msgstr "Animaatio: muuta siirtymää"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
-msgstr "Animaatio: muuta siirtymää"
+msgstr "Animaatio: muuta muunnosta"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
@@ -50,7 +48,7 @@ msgstr "Animaatio: muuta avainruudun arvoa"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Animaatio: Muuta kutsua"
+msgstr "Animaatio: muuta kutsua"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
@@ -70,7 +68,7 @@ msgstr "Siirrä animaatioraita alas"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr "Poista animaation raita"
+msgstr "Poista animaatioraita"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
@@ -78,24 +76,23 @@ msgstr "Aseta siirtymät:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr "Nimeä animaatioraita uudelleen"
+msgstr "Animaatioraita: nimeä uudelleen"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr "Animaatio: Vaihda raidan interpolaatiota"
+msgstr "Animaatioraita: muuta interpolaatiota"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr "Animaatio: Muuta avainta tila"
+msgstr "Animaatioraita: muuta arvon tilaa"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Track Change Wrap Mode"
-msgstr "Animaatio: Muuta toisto tila"
+msgstr "Animaatioraita: muuta kierron tilaa"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
-msgstr "Muokkaa noden käyrää"
+msgstr "Muokkaa solmun käyrää"
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
@@ -103,16 +100,16 @@ msgstr "Muokkaa valinnan käyrää"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr "Poista avaimet"
+msgstr "Animaatio: poista avaimet"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr "Monista valinta"
+msgstr "Kahdenna valinta"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr "Monista käänteisesti"
+msgstr "Kahdenna käänteisesti"
#: editor/animation_editor.cpp
msgid "Remove Selection"
@@ -132,11 +129,11 @@ msgstr "Liipaisin"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr "Lisää avain"
+msgstr "Animaatio: lisää avain"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr "SIirrä avaimia"
+msgstr "Animaatio: siirrä avaimia"
#: editor/animation_editor.cpp
msgid "Scale Selection"
@@ -193,7 +190,7 @@ msgstr "Siivoa animaatio"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
-msgstr "Luo UUSI raita %lle ja lisää avain?"
+msgstr "Luo kohteelle %s UUSI raita ja lisää avain?"
#: editor/animation_editor.cpp
msgid "Create %d NEW tracks and insert keys?"
@@ -209,7 +206,7 @@ msgstr "Luo"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr "Animaatio: Luo ja lisää"
+msgstr "Animaatio: luo ja lisää"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
@@ -221,7 +218,7 @@ msgstr "Animaatio: Lisää avain"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr "Vaihda animaation pituutta"
+msgstr "Muuta animaation pituutta"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
@@ -233,7 +230,7 @@ msgstr "Animaatio: Luo tyypitetty arvoavain"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr "Animaatio: Lisää"
+msgstr "Animaatio: lisää"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
@@ -257,7 +254,7 @@ msgstr "Animaation pituus (sekunteina)."
#: editor/animation_editor.cpp
msgid "Step (s):"
-msgstr "Askellus:"
+msgstr "Askellus (s):"
#: editor/animation_editor.cpp
msgid "Cursor step snap (in seconds)."
@@ -265,7 +262,7 @@ msgstr "Kohdistimen askelrajoitin (sekunneissa)."
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
-msgstr "Ota käyttöön/poista käytöstä animaation toisto."
+msgstr "Ota käyttöön tai poista käytöstä animaation toisto."
#: editor/animation_editor.cpp
msgid "Add new tracks."
@@ -289,7 +286,7 @@ msgstr "Raidan työkalut"
#: editor/animation_editor.cpp
msgid "Enable editing of individual keys by clicking them."
-msgstr "Mahdollistaa avainten muokkaamisen klikkaamalla."
+msgstr "Mahdollistaa avainten muokkaamisen napsauttamalla niitä."
#: editor/animation_editor.cpp
msgid "Anim. Optimizer"
@@ -301,11 +298,11 @@ msgstr "Max. lineaarinen virhe:"
#: editor/animation_editor.cpp
msgid "Max. Angular Error:"
-msgstr "Max. Kulmavirhe:"
+msgstr "Max. kulmavirhe:"
#: editor/animation_editor.cpp
msgid "Max Optimizable Angle:"
-msgstr "Max. Optimoitava kulma:"
+msgstr "Max. optimoitava kulma:"
#: editor/animation_editor.cpp
msgid "Optimize"
@@ -313,7 +310,7 @@ msgstr "Optimoi"
#: editor/animation_editor.cpp
msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
-msgstr "Valitse AnimationPlayer Scenepuusta muokataksesi animaatioita."
+msgstr "Valitse AnimationPlayer skenen puusta muokataksesi animaatioita."
#: editor/animation_editor.cpp
msgid "Key"
@@ -329,7 +326,7 @@ msgstr "Skaalaussuhde:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr "Mistä nodesta kutsutaan funktiota?"
+msgstr "Mistä solmusta kutsutaan funktiota?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -345,7 +342,7 @@ msgstr "Siivoa kaikki animaatiot"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation(s) (NO UNDO!)"
-msgstr "Siivoa animaatio(t) (EI VOI KUMOTA)"
+msgstr "Siivoa animaatio(t) (EI VOI KUMOTA!)"
#: editor/animation_editor.cpp
msgid "Clean-Up"
@@ -369,7 +366,7 @@ msgstr "Mene riville"
#: editor/code_editor.cpp
msgid "Line Number:"
-msgstr "RIvinumero:"
+msgstr "Rivinumero:"
#: editor/code_editor.cpp
msgid "No Matches"
@@ -417,23 +414,23 @@ msgstr "Rivi:"
#: editor/code_editor.cpp
msgid "Col:"
-msgstr "Kolumni:"
+msgstr "Sarake:"
#: editor/connections_dialog.cpp
msgid "Method in target Node must be specified!"
-msgstr "Kohdenoden metodi täytyy määrittää!"
+msgstr "Kohdesolmun metodi täytyy määrittää!"
#: editor/connections_dialog.cpp
msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
msgstr ""
-"Kohde metodia ei löytynyt! Määrittele voimassa oleva metodi tai kiinnitä "
-"skripti nodeen."
+"Kohdemetodia ei löytynyt! Määrittele voimassa oleva metodi tai kiinnitä "
+"skripti solmuun."
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
-msgstr "Yhdistä Nodeen:"
+msgstr "Yhdistä solmuun:"
#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
@@ -458,7 +455,7 @@ msgstr "Ylimääräiset argumentit:"
#: editor/connections_dialog.cpp
msgid "Path to Node:"
-msgstr "Polku Nodeen:"
+msgstr "Polku solmuun:"
#: editor/connections_dialog.cpp
msgid "Make Function"
@@ -492,7 +489,7 @@ msgstr "Yhdistä"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr "Yhdistä '%s' '%s':n"
+msgstr "Yhdistä solmu '%s' solmuun '%s'"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
@@ -500,7 +497,7 @@ msgstr "Yhdistävä signaali:"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
-msgstr "Katkaise yhteys '%s' '%s':n"
+msgstr "Katkaise yhteys solmusta '%s' solmuun '%s'"
#: editor/connections_dialog.cpp
msgid "Connect..."
@@ -569,7 +566,7 @@ msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will not take effect unless reloaded."
msgstr ""
-"Sceneä '%s' muokataan parhaillaan.\n"
+"Skeneä '%s' muokataan parhaillaan.\n"
"Muutokset tulevat voimaan vasta päivityksen jälkeen."
#: editor/dependency_editor.cpp
@@ -646,7 +643,7 @@ msgstr "Virhe ladatessa:"
#: editor/dependency_editor.cpp
msgid "Scene failed to load due to missing dependencies:"
-msgstr "Scenen lataaminen epäonnistui puuttuvan riippuvuuden takia:"
+msgstr "Skenen lataaminen epäonnistui puuttuvan riippuvuuden takia:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Open Anyway"
@@ -665,9 +662,8 @@ msgid "Errors loading!"
msgstr "Virheitä ladatessa!"
#: editor/dependency_editor.cpp
-#, fuzzy
msgid "Permanently delete %d item(s)? (No undo!)"
-msgstr "Poista pysyvästi %d ? (Ei voi kumota!)"
+msgstr "Poista pysyvästi %d kohdetta? (Ei voi kumota!)"
#: editor/dependency_editor.cpp
msgid "Owns"
@@ -678,9 +674,8 @@ msgid "Resources Without Explicit Ownership:"
msgstr "Resurssit, joilla ei ole selvää omistajaa:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
-#, fuzzy
msgid "Orphan Resource Explorer"
-msgstr "Orpojen resurssien selain"
+msgstr "Irrallisten resurssien hallinta"
#: editor/dependency_editor.cpp
msgid "Delete selected files?"
@@ -736,27 +731,27 @@ msgstr "Tekijät"
#: editor/editor_about.cpp
msgid "Platinum Sponsors"
-msgstr "Platinum sponsorit"
+msgstr "Platinasponsorit"
#: editor/editor_about.cpp
msgid "Gold Sponsors"
-msgstr "Kulta sponsorit"
+msgstr "Kultasponsorit"
#: editor/editor_about.cpp
msgid "Mini Sponsors"
-msgstr "Mini sponsorit"
+msgstr "Minisponsorit"
#: editor/editor_about.cpp
msgid "Gold Donors"
-msgstr "Kulta lahjoittajat"
+msgstr "Kultalahjoittajat"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr "Hopea lahjoittajat"
+msgstr "Hopealahjoittajat"
#: editor/editor_about.cpp
msgid "Bronze Donors"
-msgstr "Pronssi lahjoittajat"
+msgstr "Pronssilahjoittajat"
#: editor/editor_about.cpp
msgid "Donors"
@@ -779,8 +774,8 @@ msgid ""
msgstr ""
"Godot moottori käyttää useita kolmannen osapuolen ilmaisia ja avoimia "
"kirjastoja, jotka kaikki ovat yhteensopivia sen MIT lisenssin kanssa. "
-"Seuraava tyhjentävä listaus sisältää kaikki tälläiset kolmannen osapuolen "
-"komponentit ja niiden vastaavat copyright ja lisenssi määritelmät."
+"Seuraava tyhjentävä listaus sisältää kaikki tällaiset kolmannen osapuolen "
+"komponentit ja niiden vastaavat tekijänoikeustiedot ja käyttöoikeusehdot."
#: editor/editor_about.cpp
msgid "All Components"
@@ -796,7 +791,7 @@ msgstr "Lisenssit"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Error opening package file, not in zip format."
-msgstr "Virhe avattaessa pakettia, ei zip muotoinen."
+msgstr "Virhe avattaessa pakettitiedostoa, ei zip-muodossa."
#: editor/editor_asset_installer.cpp
msgid "Uncompressing Assets"
@@ -830,7 +825,7 @@ msgstr "Lisää efekti"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
-msgstr "Nimeä väylä uudelleen"
+msgstr "Nimeä ääniväylä uudelleen"
#: editor/editor_audio_buses.cpp
msgid "Change Audio Bus Volume"
@@ -919,7 +914,7 @@ msgstr "Monista ääniväylä"
#: editor/editor_audio_buses.cpp
msgid "Reset Bus Volume"
-msgstr "Palauta äänenvoimakkuus"
+msgstr "Palauta väylän äänenvoimakkuus"
#: editor/editor_audio_buses.cpp
msgid "Move Audio Bus"
@@ -1014,18 +1009,16 @@ msgid "File does not exist."
msgstr "Tiedostoa ei ole olemassa."
#: editor/editor_autoload_settings.cpp
-#, fuzzy
msgid "Not in resource path."
msgstr "Ei löytynyt resurssipolusta."
#: editor/editor_autoload_settings.cpp
-#, fuzzy
msgid "Add AutoLoad"
msgstr "Lisää automaattisesti ladattava"
#: editor/editor_autoload_settings.cpp
msgid "Autoload '%s' already exists!"
-msgstr "Automaattisesti ladattava '%s' löytyi jo!"
+msgstr "Automaattisesti ladattava '%s' on jo olemassa!"
#: editor/editor_autoload_settings.cpp
msgid "Rename Autoload"
@@ -1033,10 +1026,9 @@ msgstr "Nimeä automaattisesti ladattava uudelleen"
#: editor/editor_autoload_settings.cpp
msgid "Toggle AutoLoad Globals"
-msgstr ""
+msgstr "Aseta globaalien automaattilataus"
#: editor/editor_autoload_settings.cpp
-#, fuzzy
msgid "Move Autoload"
msgstr "Siirrä automaattisesti ladattavaa"
@@ -1059,7 +1051,7 @@ msgstr "Polku:"
#: editor/editor_autoload_settings.cpp
msgid "Node Name:"
-msgstr "Noden nimi:"
+msgstr "Solmun nimi:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
@@ -1067,9 +1059,8 @@ msgid "Name"
msgstr "Nimi"
#: editor/editor_autoload_settings.cpp
-#, fuzzy
msgid "Singleton"
-msgstr "Ainokainen"
+msgstr "Singleton"
#: editor/editor_data.cpp
msgid "Updating Scene"
@@ -1150,7 +1141,7 @@ msgstr "Näytä tiedostonhallinnassa"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "New Folder..."
-msgstr "Luo kansio..."
+msgstr "Uusi kansio..."
#: editor/editor_file_dialog.cpp
msgid "Refresh"
@@ -1207,19 +1198,16 @@ msgid "Toggle Hidden Files"
msgstr "Näytä piilotiedostot"
#: editor/editor_file_dialog.cpp
-#, fuzzy
msgid "Toggle Favorite"
-msgstr "Näytä suosikit"
+msgstr "Aseta suosikiksi"
#: editor/editor_file_dialog.cpp
-#, fuzzy
msgid "Toggle Mode"
-msgstr "Näytä/piilota"
+msgstr "Aseta tila"
#: editor/editor_file_dialog.cpp
-#, fuzzy
msgid "Focus Path"
-msgstr "Kohdista polku"
+msgstr "Kohdista polkuun"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
@@ -1235,7 +1223,7 @@ msgstr "Siirry yläkansioon"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Directories & Files:"
-msgstr "Hakemistot & tiedostot:"
+msgstr "Hakemistot ja tiedostot:"
#: editor/editor_file_dialog.cpp
msgid "Preview:"
@@ -1252,11 +1240,11 @@ msgstr "Käytä sopivaa tiedostopäätettä."
#: editor/editor_file_system.cpp
msgid "ScanSources"
-msgstr ""
+msgstr "Selaa lähdetiedostoja"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr "Tuodaan (uudelleen) Assetteja"
+msgstr "Tuodaan (uudelleen) assetteja"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
@@ -1273,7 +1261,7 @@ msgstr "Etsi luokkia"
#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
msgid "Top"
-msgstr "Pinta"
+msgstr "Yläpuoli"
#: editor/editor_help.cpp editor/property_editor.cpp
msgid "Class:"
@@ -1284,9 +1272,8 @@ msgid "Inherits:"
msgstr "Perii:"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Inherited by:"
-msgstr "Peritty:"
+msgstr "Perivät:"
#: editor/editor_help.cpp
msgid "Brief Description:"
@@ -1371,8 +1358,8 @@ msgid ""
"There is currently no description for this property. Please help us by "
"[color=$color][url=$url]contributing one[/url][/color]!"
msgstr ""
-"Tälle ei vielä löydy kuvailua. Voit auttaa meitä [color=$color][url="
-"$url]kirjoittamalla sellaisen[/url][/color]!"
+"Tälle ominaisuudelle ei vielä löydy kuvausta. Voit auttaa meitä [color="
+"$color][url=$url]kirjoittamalla sellaisen[/url][/color]!"
#: editor/editor_help.cpp
msgid "Methods"
@@ -1387,7 +1374,7 @@ msgid ""
"There is currently no description for this method. Please help us by [color="
"$color][url=$url]contributing one[/url][/color]!"
msgstr ""
-"Tälle metodille ei vielä löydy kuvailua. Voit auttaa meitä [color=$color]"
+"Tälle metodille ei vielä löydy kuvausta. Voit auttaa meitä [color=$color]"
"[url=$url]kirjoittamalla sellaisen[/url][/color]!"
#: editor/editor_help.cpp
@@ -1464,7 +1451,7 @@ msgstr "Virhe ladattaessa tiedostoa '%s'."
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr "Tallennetaan sceneä"
+msgstr "Tallennetaan skeneä"
#: editor/editor_node.cpp
msgid "Analyzing"
@@ -1492,19 +1479,19 @@ msgstr "Resurssin lataaminen epäonnistui."
#: editor/editor_node.cpp
msgid "Can't load MeshLibrary for merging!"
-msgstr "MalliKirjastojen yhdistäminen ei onnistunut!"
+msgstr "Ei voitu ladata MeshLibrary resurssia yhdistämistä varten!"
#: editor/editor_node.cpp
msgid "Error saving MeshLibrary!"
-msgstr "Virhe tallennettaessa MeshLibrarya!"
+msgstr "Virhe tallennettaessa MeshLibrary resurssia!"
#: editor/editor_node.cpp
msgid "Can't load TileSet for merging!"
-msgstr "Ei voida ladata tilesetiä tuontia varten!"
+msgstr "Ei voida ladata ruutuvalikoimaa yhdistämistä varten!"
#: editor/editor_node.cpp
msgid "Error saving TileSet!"
-msgstr "Virhe tallennettaessa tilesetiä!"
+msgstr "Virhe tallennettaessa ruutuvalikoimaa!"
#: editor/editor_node.cpp
msgid "Error trying to save layout!"
@@ -1512,16 +1499,15 @@ msgstr "Virhe tallennettaessa asettelua!"
#: editor/editor_node.cpp
msgid "Default editor layout overridden."
-msgstr "Editorin oletusulkoasu ylikirjoitettu."
+msgstr "Editorin oletusasettelu ylikirjoitettu."
#: editor/editor_node.cpp
msgid "Layout name not found!"
-msgstr "Layoutin nimeä ei löytynyt!"
+msgstr "Asettelun nimeä ei löytynyt!"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Restored default layout to base settings."
-msgstr "Palautettiin oletusasettelu alkuperäiseen muotoonsa."
+msgstr "Palautettiin oletusasettelu alkuperäisiin asetuksiinsa."
#: editor/editor_node.cpp
msgid ""
@@ -1530,7 +1516,7 @@ msgid ""
"understand this workflow."
msgstr ""
"Tämä resurssi kuuluu tuotuun skeneen, joten sitä ei voi suoraan muokata.\n"
-"Lue ohjeet skenejen tuomisesta, jotta ymmärrät paremmin tämän työkulun."
+"Lue ohjeet skenejen tuomisesta, jotta ymmärrät paremmin tämän työnkulun."
#: editor/editor_node.cpp
msgid ""
@@ -1596,7 +1582,6 @@ msgid "Copy Resource"
msgstr "Kopioi resurssi"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Make Built-In"
msgstr "Tee sisäänrakennettu"
@@ -1610,7 +1595,7 @@ msgstr "Avaa ohjeessa"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr "Suoritettavaa sceneä ei ole määritetty."
+msgstr "Suoritettavaa skeneä ei ole määritetty."
#: editor/editor_node.cpp
msgid ""
@@ -1627,8 +1612,8 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"Valittua sceneä '%s' ei ole olemassa, valitse kelvollinen?\n"
-"Voit muuttaa sitä myöhemmin projektin asetuksista."
+"Valittua skeneä '%s' ei ole olemassa, valitse kelvollinen?\n"
+"Voit muuttaa sitä myöhemmin projektin asetuksista, kohdasta 'Application'."
#: editor/editor_node.cpp
msgid ""
@@ -1636,13 +1621,13 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"Valittu scene '%s' ei ole scene-tiedosto, valitse kelvollinen?\n"
-"Voit muuttaa sitä myöhemmin projektin asetuksista."
+"Valittu skene '%s' ei ole scene-tiedosto, valitse kelvollinen?\n"
+"Voit muuttaa sitä myöhemmin projektin asetuksista, kohdasta 'Application'."
#: editor/editor_node.cpp
msgid "Current scene was never saved, please save it prior to running."
msgstr ""
-"Nykyistä sceneä ei ole vielä tallennettu. Tallenna se ennen suorittamista."
+"Nykyistä skeneä ei ole vielä tallennettu. Tallenna se ennen suorittamista."
#: editor/editor_node.cpp
msgid "Could not start subprocess!"
@@ -1650,19 +1635,19 @@ msgstr "Aliprosessia ei voitu käynnistää!"
#: editor/editor_node.cpp
msgid "Open Scene"
-msgstr "Avaa scene"
+msgstr "Avaa skene"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr "Avaa kantascene"
+msgstr "Avaa kantaskene"
#: editor/editor_node.cpp
msgid "Quick Open Scene..."
-msgstr "Nopea skenen avaus..."
+msgstr "Skenen pika-avaus..."
#: editor/editor_node.cpp
msgid "Quick Open Script..."
-msgstr "Nopea skriptin avaus..."
+msgstr "Skriptin pika-avaus..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1674,7 +1659,7 @@ msgstr "Tallennetaanko muutokset tiedostoon '%s' ennen sulkemista?"
#: editor/editor_node.cpp
msgid "Save Scene As..."
-msgstr "Tallenna scene nimellä..."
+msgstr "Tallenna skene nimellä..."
#: editor/editor_node.cpp
msgid "No"
@@ -1686,35 +1671,35 @@ msgstr "Kyllä"
#: editor/editor_node.cpp
msgid "This scene has never been saved. Save before running?"
-msgstr "Tätä sceneä ei ole koskaan tallennettu. Tallenna ennen suorittamista?"
+msgstr "Tätä skeneä ei ole koskaan tallennettu. Tallenna ennen suorittamista?"
#: editor/editor_node.cpp editor/scene_tree_dock.cpp
msgid "This operation can't be done without a scene."
-msgstr "Tätä toimintoa ei voi tehdä ilman sceneä."
+msgstr "Tätä toimintoa ei voi tehdä ilman skeneä."
#: editor/editor_node.cpp
msgid "Export Mesh Library"
-msgstr "Vie malli kirjasto"
+msgstr "Vie mesh-kirjasto"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr "Tätä toimintoa ei voida suorittaa ilman päänodea."
+msgstr "Tätä toimintoa ei voida suorittaa ilman juurisolmua."
#: editor/editor_node.cpp
msgid "Export Tile Set"
-msgstr "Vie tileset"
+msgstr "Vie ruutuvalikoima"
#: editor/editor_node.cpp
msgid "This operation can't be done without a selected node."
-msgstr "Tätä toimintoa ei voi tehdä ilman valittua nodea."
+msgstr "Tätä toimintoa ei voi tehdä ilman valittua solmua."
#: editor/editor_node.cpp
msgid "Current scene not saved. Open anyway?"
-msgstr "Nykyistä sceneä ei ole tallennettu. Avaa joka tapauksessa?"
+msgstr "Nykyistä skeneä ei ole tallennettu. Avaa joka tapauksessa?"
#: editor/editor_node.cpp
msgid "Can't reload a scene that was never saved."
-msgstr "Ei voida uudelleen ladata skeneä jota ei ole vielä tallennettu."
+msgstr "Ei voida ladata uudelleen skeneä, jota ei ole koskaan tallennettu."
#: editor/editor_node.cpp
msgid "Revert"
@@ -1726,7 +1711,7 @@ msgstr "Tätä toimintoa ei voida peruttaa. Palauta joka tapauksessa?"
#: editor/editor_node.cpp
msgid "Quick Run Scene..."
-msgstr "Nopea skenen käynnistys..."
+msgstr "Skenen pikakäynnistys..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1764,7 +1749,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Pick a Main Scene"
-msgstr "Valitse pääscene"
+msgstr "Valitse pääskene"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
@@ -1793,8 +1778,8 @@ msgid ""
"Scene '%s' was automatically imported, so it can't be modified.\n"
"To make changes to it, a new inherited scene can be created."
msgstr ""
-"Scene '%s' tuotiin automaattisesti, joten sitä ei voida muokata.\n"
-"Muokataksesi sitä voit luoda uuden perityn Scenen."
+"Skene '%s' tuotiin automaattisesti, joten sitä ei voida muokata.\n"
+"Muokataksesi sitä voit luoda uuden perityn skenen."
#: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -1806,20 +1791,20 @@ msgid ""
"Error loading scene, it must be inside the project path. Use 'Import' to "
"open the scene, then save it inside the project path."
msgstr ""
-"Virhe Scenen latauksessa, sen täytyy sijaita projektin polussa. Käytä 'Tuo' -"
-"toimintoa avataksesi Scenen ja tallenna se projektin polkuun."
+"Virhe skenen latauksessa, sen täytyy sijaita projektin polussa. Käytä 'Tuo'-"
+"toimintoa avataksesi skenen ja tallenna se projektin polkuun."
#: editor/editor_node.cpp
msgid "Scene '%s' has broken dependencies:"
-msgstr "Scenellä '%s' on rikkinäisiä riippuvuuksia:"
+msgstr "Skenellä '%s' on rikkinäisiä riippuvuuksia:"
#: editor/editor_node.cpp
msgid "Clear Recent Scenes"
-msgstr "Tyhjennä viimeiset scenet"
+msgstr "Tyhjennä viimeisimmät skenet"
#: editor/editor_node.cpp
msgid "Save Layout"
-msgstr "Tallenna asettelut"
+msgstr "Tallenna asettelu"
#: editor/editor_node.cpp
msgid "Delete Layout"
@@ -1832,7 +1817,7 @@ msgstr "Oletus"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr "Vaihda Scenen välilehteä"
+msgstr "Vaihda skenen välilehteä"
#: editor/editor_node.cpp
msgid "%d more files or folders"
@@ -1884,7 +1869,7 @@ msgstr "Suodata tiedostot..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
-msgstr "Toiminnot skene tiedostoille."
+msgstr "Toiminnot skenetiedostoille."
#: editor/editor_node.cpp
msgid "New Scene"
@@ -1920,11 +1905,11 @@ msgstr "Muunna..."
#: editor/editor_node.cpp
msgid "MeshLibrary..."
-msgstr "MalliKirjasto..."
+msgstr "Mesh-kirjastoksi..."
#: editor/editor_node.cpp
msgid "TileSet..."
-msgstr ""
+msgstr "Ruutuvalikoimaksi..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1970,7 +1955,7 @@ msgstr "Lopeta ja palaa projektiluetteloon"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Debug"
-msgstr "Testaa"
+msgstr "Virheenkorjaus"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
@@ -1981,8 +1966,8 @@ msgid ""
"When exporting or deploying, the resulting executable will attempt to "
"connect to the IP of this computer in order to be debugged."
msgstr ""
-"Vietäessä tai julkaistaessa, käynnistystiedosto yrittää ottaa yhteyden tämän "
-"tietokoneen IP osoitteeseen testaamista varten."
+"Vietäessä tai julkaistaessa, käynnistettävä ohjelma yrittää ottaa yhteyden "
+"tämän tietokoneen IP-osoitteeseen testaamista varten."
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
@@ -2013,7 +1998,7 @@ msgid ""
"Collision shapes and raycast nodes (for 2D and 3D) will be visible on the "
"running game if this option is turned on."
msgstr ""
-"Osuma-alueet ja raycast nodet (2D ja 3D) ovat näkyvillä peliä ajettaessa "
+"Törmäysmuodot ja raycast-solmut (2D ja 3D) ovat näkyvillä peliä ajettaessa "
"tämän ollessa valittuna."
#: editor/editor_node.cpp
@@ -2041,8 +2026,7 @@ msgid ""
msgstr ""
"Tämän ollessa valittuna, kaikki skeneen tehdyt muutokset toteutetaan myös "
"käynnissä olevassa pelissä.\n"
-"Tämä on tehokkainta verkkotiedostojärjestelmän kanssa mikäli käytössä on "
-"etälaite."
+"Mikäli peliä ajetaan etälaitteella, on tehokkaampaa käyttää verkkolevyä."
#: editor/editor_node.cpp
msgid "Sync Script Changes"
@@ -2057,7 +2041,7 @@ msgid ""
msgstr ""
"Jos tämä on valittu, kaikki tallennetut skriptit ladataan uudelleen pelin "
"käynnistyessä.\n"
-"Mikäli peli ajetaan etälaitteella, on tehokkaampaa käyttää verkkolevyä."
+"Mikäli peliä ajetaan etälaitteella, on tehokkaampaa käyttää verkkolevyä."
#: editor/editor_node.cpp
msgid "Editor"
@@ -2124,15 +2108,15 @@ msgstr "Pelaa"
#: editor/editor_node.cpp
msgid "Pause the scene"
-msgstr "Pysäytä Scene"
+msgstr "Keskeytä skenen suorittaminen hetkellisesti"
#: editor/editor_node.cpp
msgid "Pause Scene"
-msgstr "Pysäytä Scene"
+msgstr "Keskeytä skene"
#: editor/editor_node.cpp
msgid "Stop the scene."
-msgstr "Lopeta Scene."
+msgstr "Lopeta skenen suorittaminen."
#: editor/editor_node.cpp
msgid "Stop"
@@ -2140,11 +2124,11 @@ msgstr "Pysäytä"
#: editor/editor_node.cpp
msgid "Play the edited scene."
-msgstr "Käynnistä muokattu skene."
+msgstr "Käynnistä muokattavana oleva skene."
#: editor/editor_node.cpp
msgid "Play Scene"
-msgstr "Toista Scene"
+msgstr "Toista skene"
#: editor/editor_node.cpp
msgid "Play custom scene"
@@ -2172,7 +2156,7 @@ msgstr "Poista päivitysanimaatio"
#: editor/editor_node.cpp
msgid "Inspector"
-msgstr "Tarkastaja"
+msgstr "Tarkastelu"
#: editor/editor_node.cpp
msgid "Create a new resource in memory and edit it."
@@ -2217,7 +2201,7 @@ msgstr "Tuo"
#: editor/editor_node.cpp
msgid "Node"
-msgstr "Node"
+msgstr "Solmu"
#: editor/editor_node.cpp
msgid "FileSystem"
@@ -2253,16 +2237,15 @@ msgstr "Salasana:"
#: editor/editor_node.cpp
msgid "Open & Run a Script"
-msgstr "Avaa & suorita skripti"
+msgstr "Avaa ja suorita skripti"
#: editor/editor_node.cpp
-#, fuzzy
msgid "New Inherited"
-msgstr "Uusi peritty Scene..."
+msgstr "Uusi peritty skene"
#: editor/editor_node.cpp
msgid "Load Errors"
-msgstr "Lataa virheet"
+msgstr "Latausvirheet"
#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
msgid "Select"
@@ -2282,7 +2265,7 @@ msgstr "Avaa skriptieditori"
#: editor/editor_node.cpp editor/project_manager.cpp
msgid "Open Asset Library"
-msgstr "Avaa Asset-kirjasto"
+msgstr "Avaa asset-kirjasto"
#: editor/editor_node.cpp
msgid "Open the next Editor"
@@ -2294,7 +2277,7 @@ msgstr "Avaa edellinen editori"
#: editor/editor_plugin.cpp
msgid "Creating Mesh Previews"
-msgstr "Luodaan mallien esikatseluita"
+msgstr "Luodaan meshien esikatseluita"
#: editor/editor_plugin.cpp
msgid "Thumbnail..."
@@ -2383,15 +2366,15 @@ msgid ""
"Please add a runnable preset in the export menu."
msgstr ""
"Käynnistettävää vientipohjaa ei löytynyt tälle alustalle.\n"
-"Lisää sellainen vienti-valikosta."
+"Lisää sellainen vientivalikosta."
#: editor/editor_run_script.cpp
msgid "Write your logic in the _run() method."
-msgstr "Kirjoita logiikka _run() -metodiin."
+msgstr "Kirjoita logiikka _run() metodiin."
#: editor/editor_run_script.cpp
msgid "There is an edited scene already."
-msgstr "Muokattu Scene on jo olemassa."
+msgstr "Muokattu skene on jo olemassa."
#: editor/editor_run_script.cpp
msgid "Couldn't instance script:"
@@ -2399,7 +2382,7 @@ msgstr "Ei voitu luoda instanssia skriptistä:"
#: editor/editor_run_script.cpp
msgid "Did you forget the 'tool' keyword?"
-msgstr "Unohditko 'tool' hakusanan?"
+msgstr "Unohditko 'tool' avainsanan?"
#: editor/editor_run_script.cpp
msgid "Couldn't run script:"
@@ -2407,7 +2390,7 @@ msgstr "Skriptiä ei voitu suorittaa:"
#: editor/editor_run_script.cpp
msgid "Did you forget the '_run' method?"
-msgstr "Unohditko '_run' -metodin?"
+msgstr "Unohditko '_run' metodin?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
@@ -2415,15 +2398,15 @@ msgstr "Oletus (sama kuin editori)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
-msgstr "Valitse tuotava(t) node(t)"
+msgstr "Valitse tuotavat solmut"
#: editor/editor_sub_scene.cpp
msgid "Scene Path:"
-msgstr "Scenen polku:"
+msgstr "Skenen polku:"
#: editor/editor_sub_scene.cpp
msgid "Import From Node:"
-msgstr "Tuo Nodesta:"
+msgstr "Tuo solmusta:"
#: editor/export_template_manager.cpp
msgid "Re-Download"
@@ -2459,15 +2442,15 @@ msgstr "Poista mallin versio '%s'?"
#: editor/export_template_manager.cpp
msgid "Can't open export templates zip."
-msgstr "Vientipohjien zip-tiedostoa ei voitu avata."
+msgstr "Vientimallien zip-tiedostoa ei voitu avata."
#: editor/export_template_manager.cpp
msgid "Invalid version.txt format inside templates."
-msgstr "Paketti sisältää viallisen version.txt tiedoston."
+msgstr "Vientimalli sisältää virheellisen version.txt tiedoston."
#: editor/export_template_manager.cpp
msgid "No version.txt found inside templates."
-msgstr "version.txt -tiedostoa ei löytynyt."
+msgstr "Vientimalleista ei löytynyt version.txt tiedostoa."
#: editor/export_template_manager.cpp
msgid "Error creating path for templates:"
@@ -2740,16 +2723,15 @@ msgstr "Merkitse kansio suosikkeihin"
#: editor/filesystem_dock.cpp
msgid "Instance the selected scene(s) as child of the selected node."
-msgstr "Luo valituista skeneistä ilmentymä valitun noden alle."
+msgstr "Luo valituista skeneistä ilmentymä valitun solmun alle."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid ""
"Scanning Files,\n"
"Please Wait..."
msgstr ""
"Selataan tiedostoja,\n"
-"Hetkinen..."
+"Hetkinen…"
#: editor/filesystem_dock.cpp
msgid "Move"
@@ -2811,21 +2793,19 @@ msgstr "Tuo useina skeneinä ja materiaaleina"
#: editor/import/resource_importer_scene.cpp
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import Scene"
-msgstr "Tuo Scene"
+msgstr "Tuo skene"
#: editor/import/resource_importer_scene.cpp
msgid "Importing Scene..."
-msgstr "Tuodaan Scene..."
+msgstr "Tuodaan skene..."
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Generating Lightmaps"
-msgstr "Muunna Lightmapiksi:"
+msgstr "Luodaan Lightmappeja"
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Generating for Mesh: "
-msgstr "Luo AABB"
+msgstr "Luodaan meshille: "
#: editor/import/resource_importer_scene.cpp
msgid "Running Custom Script..."
@@ -2874,7 +2854,7 @@ msgstr "Tuo uudelleen"
#: editor/multi_node_edit.cpp
msgid "MultiNode Set"
-msgstr ""
+msgstr "Aseta usealle solmulle"
#: editor/node_dock.cpp
msgid "Groups"
@@ -2882,7 +2862,7 @@ msgstr "Ryhmät"
#: editor/node_dock.cpp
msgid "Select a Node to edit Signals and Groups."
-msgstr "Valitse node jonka signaaleja ja ryhmiä haluat muokata."
+msgstr "Valitse solmu, jonka signaaleja ja ryhmiä haluat muokata."
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -3030,11 +3010,11 @@ msgstr "Toista valittu animaatio nykyisestä kohdasta. (D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation position (in seconds)."
-msgstr "Animaation sijainti (sekunneissa)."
+msgstr "Animaation kohta (sekunneissa)."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Scale animation playback globally for the node."
-msgstr "Skaalaa animaation toistoa globaalisti nodelle."
+msgstr "Skaalaa animaation toistoa globaalisti solmulle."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create new animation in player."
@@ -3058,7 +3038,7 @@ msgstr "Näytä lista animaatioista soittimessa."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Autoplay on Load"
-msgstr "Toista automaattisesti"
+msgstr "Toista automaattisesti ladattaessa"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
@@ -3078,7 +3058,7 @@ msgstr "Onion skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Enable Onion Skinning"
-msgstr "Käytä Onion skinningiä"
+msgstr "Käytä onion skinningiä"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Directions"
@@ -3179,7 +3159,7 @@ msgstr "Sulauta"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix"
-msgstr "Sekoitus"
+msgstr "Sekoita"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Auto Restart:"
@@ -3228,11 +3208,11 @@ msgstr "Lisää syöte"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Clear Auto-Advance"
-msgstr ""
+msgstr "Poista automaattinen eteneminen"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Set Auto-Advance"
-msgstr ""
+msgstr "Aseta automaattinen eteneminen"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Delete Input"
@@ -3248,39 +3228,39 @@ msgstr "Animaatiopuu ei ole kelvollinen."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation Node"
-msgstr "Animaationode"
+msgstr "Animaatiosolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "OneShot Node"
-msgstr "OneShot node"
+msgstr "Vaiheistussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix Node"
-msgstr "Mix Node"
+msgstr "Sekoitussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend2 Node"
-msgstr "Sulautus2 node"
+msgstr "2-sulautussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend3 Node"
-msgstr "Sulautus3 node"
+msgstr "3-sulautussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend4 Node"
-msgstr "Sulautus4 node"
+msgstr "4-sulautussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeScale Node"
-msgstr ""
+msgstr "Ajanskaalaussolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeSeek Node"
-msgstr ""
+msgstr "Ajanhakusolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Transition Node"
-msgstr "Siirtymänode"
+msgstr "Siirtymäsolmu"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Import Animations..."
@@ -3288,16 +3268,15 @@ msgstr "Tuo animaatiot..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
-msgstr "Muokkaa noden suodattimia"
+msgstr "Muokkaa solmun suodattimia"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Filters..."
msgstr "Suodattimet..."
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "AnimationTree"
-msgstr "Animaatio"
+msgstr "Animaatiopuu"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
@@ -3435,7 +3414,6 @@ msgid "Official"
msgstr "Virallinen"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Testing"
msgstr "Testaus"
@@ -3449,21 +3427,27 @@ msgid ""
"Save your scene (for images to be saved in the same dir), or pick a save "
"path from the BakedLightmap properties."
msgstr ""
+"Lightmap-kuvien tallennuspolun määrittäminen ei onnistu.\n"
+"Tallenna skenesi (jotta kuvat tallentuisivat samaan hakemistoon), tai "
+"valitse tallennuspolku BakedLightmapin asetuksista."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid ""
"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake "
"Light' flag is on."
msgstr ""
+"Ei meshejä kehitettävänä. Varmista, että ne sisältävät UV2-kanavan, ja että "
+"'Bake Light' asetus on päällä."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
msgstr ""
+"Lightmap-kuvien luonti epäonnistui, varmista, että polku on "
+"kirjoituskelpoinen."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
-#, fuzzy
msgid "Bake Lightmaps"
-msgstr "Muunna Lightmapiksi:"
+msgstr "Kehitä Lightmapit"
#: editor/plugins/camera_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3572,7 +3556,7 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+RMB: Depth list selection"
-msgstr ""
+msgstr "Alt + Hiiren oikea painike: Syvyyslistan valinta"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
@@ -3588,6 +3572,8 @@ msgid ""
"Show a list of all objects at the position clicked\n"
"(same as Alt+RMB in select mode)."
msgstr ""
+"Näytä lista kaikista napsautetussa kohdassa olevista objekteista\n"
+"(sama kuin Alt + Hiiren oikea painike valintatilassa)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Click to change object's rotation pivot."
@@ -3640,15 +3626,15 @@ msgstr "Tartu isäntään"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
-msgstr "Tartu noden ankkuriin"
+msgstr "Tartu solmun ankkuriin"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
-msgstr "Tartu noden reunoihin"
+msgstr "Tartu solmun reunoihin"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to other nodes"
-msgstr "Tartu muihin nodeihin"
+msgstr "Tartu muihin solmuihin"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to guides"
@@ -3736,7 +3722,7 @@ msgstr "Asettelu"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Keys"
-msgstr "Lisää keyframeja"
+msgstr "Lisää avainruutuja"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key"
@@ -3744,7 +3730,7 @@ msgstr "Lisää keyframe"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key (Existing Tracks)"
-msgstr "Lisää keyframe (olemassaolevalle raidalle)"
+msgstr "Lisää avainruutu (olemassa olevat raidat)"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Copy Pose"
@@ -3755,9 +3741,8 @@ msgid "Clear Pose"
msgstr "Tyhjennä asento"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Drag pivot from mouse position"
-msgstr "Rahaa pistettä hiiren sijainnista"
+msgstr "Vedä keskipistettä hiiren sijainnista"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Set pivot at mouse position"
@@ -3785,12 +3770,12 @@ msgstr "Ok"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr "Ei voida luoda ilmentymiä useasta nodesta ilman juurta."
+msgstr "Ei voida luoda ilmentymiä useasta solmusta ilman juurta."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Create Node"
-msgstr "Luo Node"
+msgstr "Luo solmu"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -3806,8 +3791,8 @@ msgid ""
"Drag & drop + Shift : Add node as sibling\n"
"Drag & drop + Alt : Change node type"
msgstr ""
-"Vedä & pudota + Shift: Lisää Node sisarena\n"
-"Vedä & pudota + Alt: Muuta Noden tyyppiä"
+"Vedä & pudota + Shift: Lisää solmu sisarena\n"
+"Vedä & pudota + Alt: Muuta solmun tyyppiä"
#: editor/plugins/collision_polygon_editor_plugin.cpp
msgid "Create Poly3D"
@@ -3833,19 +3818,19 @@ msgstr "Poista valitut kohteet"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import from Scene"
-msgstr "Tuo Scenestä"
+msgstr "Tuo skenestä"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Update from Scene"
-msgstr "Päivitä Scenestä"
+msgstr "Päivitä skenestä"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat0"
-msgstr ""
+msgstr "Tasainen0"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat1"
-msgstr ""
+msgstr "Tasainen1"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease in"
@@ -3905,7 +3890,7 @@ msgstr "Pidä shift pohjassa muokataksesi tangentteja yksitellen"
#: editor/plugins/gi_probe_editor_plugin.cpp
msgid "Bake GI Probe"
-msgstr ""
+msgstr "Kehitä GI Probe"
#: editor/plugins/gradient_editor_plugin.cpp
msgid "Add/Remove Color Ramp Point"
@@ -3933,12 +3918,12 @@ msgid ""
"No OccluderPolygon2D resource on this node.\n"
"Create and assign one?"
msgstr ""
-"Tälle nodelle ei ole OccluderPolygon2D resurssia.\n"
-"Luodaanko sellainen?"
+"Tälle solmulle ei ole OccluderPolygon2D resurssia.\n"
+"Luodaanko ja asetetaanko sellainen?"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Occluder Polygon"
-msgstr "Luo Occluder polygooni"
+msgstr "Luo peittävä polygoni"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create a new polygon from scratch."
@@ -3990,15 +3975,15 @@ msgstr "Luo navigointiverkko"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Contained Mesh is not of type ArrayMesh."
-msgstr ""
+msgstr "Sisällytetty Mesh ei ole tyyppiä ArrayMesh."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "UV Unwrap failed, mesh may not be manifold?"
-msgstr ""
+msgstr "UV-aukaisu epäonnistui, mesh ei ehkä ole jaettavissa osiin?"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "No mesh to debug."
-msgstr ""
+msgstr "Ei meshiä debugattavaksi."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Model has no UV in this layer"
@@ -4006,7 +3991,7 @@ msgstr "Mallilla ei ole UV-kanavaa tällä kerroksella"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "MeshInstance lacks a Mesh!"
-msgstr "MeshInstance nodelta puuttuu Mesh!"
+msgstr "MeshInstance solmulta puuttuu Mesh!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh has not surface to create outlines from!"
@@ -4026,7 +4011,7 @@ msgstr "Luo ääriviivat"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh"
-msgstr ""
+msgstr "Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Static Body"
@@ -4046,7 +4031,7 @@ msgstr "Luo konveksi törmäysmuoto sisareksi"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline Mesh..."
-msgstr ""
+msgstr "Luo reunoista Mesh..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
@@ -4058,36 +4043,37 @@ msgstr "Näytä UV2"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Unwrap UV2 for Lightmap/AO"
-msgstr ""
+msgstr "Aukaise UV2 Lightmapille tai AO:lle"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline Mesh"
-msgstr ""
+msgstr "Luo reunoista Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Outline Size:"
msgstr "Ääriviivojen koko:"
#: editor/plugins/multimesh_editor_plugin.cpp
-#, fuzzy
msgid "No mesh source specified (and no MultiMesh set in node)."
-msgstr "Mesh:in lähdettä ei määritetty"
+msgstr ""
+"Meshin lähdettä ei ole määritetty (ja MultiMesh ei ole asetettu solmulle)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No mesh source specified (and MultiMesh contains no Mesh)."
msgstr ""
+"Meshin lähdettä ei ole määritetty (ja MultiMesh ei sisällä Mesh solmua)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (invalid path)."
-msgstr "Virheellinen Mesh:in lähde (virheellinen polku)."
+msgstr "Meshin lähde on virheellinen (virheellinen polku)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (not a MeshInstance)."
-msgstr ""
+msgstr "Meshin lähde on virheellinen (ei MeshInstance)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (contains no Mesh resource)."
-msgstr ""
+msgstr "Meshin lähde on virheellinen (ei sisällä Mesh resurssia)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No surface source specified."
@@ -4107,7 +4093,7 @@ msgstr "Pinnan lähde on virheellinen (tahkot puuttuvat)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Parent has no solid faces to populate."
-msgstr ""
+msgstr "Lähteellä ei ole kiinteitä tahkoja täytettäväksi."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Couldn't map area."
@@ -4115,7 +4101,7 @@ msgstr "Aluetta ei voitu kartoittaa."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Select a Source Mesh:"
-msgstr ""
+msgstr "Valitse lähdemesh:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Select a Target Surface:"
@@ -4127,7 +4113,7 @@ msgstr "Täytä pinta"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate MultiMesh"
-msgstr ""
+msgstr "Täytä MultiMesh"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Target Surface:"
@@ -4135,7 +4121,7 @@ msgstr "Kohdepinta:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Source Mesh:"
-msgstr ""
+msgstr "Lähde Mesh:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "X-Axis"
@@ -4151,7 +4137,7 @@ msgstr "Z-akseli"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh Up Axis:"
-msgstr ""
+msgstr "Meshin ylös-akseli:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Random Rotation:"
@@ -4167,7 +4153,7 @@ msgstr "Satunnainen skaalaus:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate"
-msgstr ""
+msgstr "Täytä"
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Bake!"
@@ -4245,6 +4231,7 @@ msgstr "Luodaan AABB"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Can only set point into a ParticlesMaterial process material"
msgstr ""
+"Piste voidaan asettaa ainoastaan ParticlesMaterial käsittelyn materiaaliin"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Error loading image:"
@@ -4252,7 +4239,7 @@ msgstr "Virhe ladattaessa kuvaa:"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "No pixels with transparency > 128 in image..."
-msgstr "Kuvassa ei ole pikseleitä, joiden läpinäkyvyys on enemmän kuin 128."
+msgstr "Kuvassa ei ole pikseleitä, joiden läpinäkyvyys on enemmän kuin 128…"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
@@ -4294,11 +4281,11 @@ msgstr "Emission väri"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry."
-msgstr "Node ei sisällä geometriaa."
+msgstr "Solmu ei sisällä geometriaa."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry (faces)."
-msgstr "Nodelta puuttuu geometria (tahkot)."
+msgstr "Solmulta puuttuu geometria (tahkot)."
#: editor/plugins/particles_editor_plugin.cpp
msgid "A processor material of type 'ParticlesMaterial' is required."
@@ -4322,10 +4309,9 @@ msgstr "Luo säteilypisteet meshistä"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr "Luo säteilypisteet nodesta"
+msgstr "Luo säteilypisteet solmusta"
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Create Emitter"
msgstr "Luo säteilijä/lähetin"
@@ -4468,16 +4454,15 @@ msgstr "Muunna UV kartta"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon 2D UV Editor"
-msgstr ""
+msgstr "Polygon 2D UV-editori"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Move Point"
msgstr "Siirrä pistettä"
#: editor/plugins/polygon_2d_editor_plugin.cpp
-#, fuzzy
msgid "Ctrl: Rotate"
-msgstr "Ctrl: Pyöritä/kierrä"
+msgstr "Ctrl: Kierrä"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift: Move All"
@@ -4493,7 +4478,7 @@ msgstr "Siirrä polygonia"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Rotate Polygon"
-msgstr "Käännä polygonia"
+msgstr "Kierrä polygonia"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Scale Polygon"
@@ -4509,11 +4494,11 @@ msgstr "Muokkaa"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon->UV"
-msgstr "Polygooni->UV"
+msgstr "Polygoni->UV"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "UV->Polygon"
-msgstr "UV->Polygooni"
+msgstr "UV->Polygoni"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Clear UV"
@@ -4720,13 +4705,12 @@ msgid "Find Next"
msgstr "Etsi seuraava"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Step Over"
-msgstr "Ohita"
+msgstr "Siirry seuraavaan"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Step Into"
-msgstr "Siirry"
+msgstr "Siirry sisään"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Break"
@@ -4751,7 +4735,7 @@ msgstr "Avaa Godotin online-dokumentaatio"
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the class hierarchy."
-msgstr "Etsi luokkahierarkia."
+msgstr "Etsi luokkahierarkiasta."
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the reference documentation."
@@ -4797,7 +4781,7 @@ msgstr "Debuggeri"
msgid ""
"Built-in scripts can only be edited when the scene they belong to is loaded"
msgstr ""
-"Sisäänrakennettuja skriptejä voi muokata ainoastaan kun Scene, johon ne "
+"Sisäänrakennettuja skriptejä voi muokata ainoastaan, kun skene, johon ne "
"kuuluvat, on ladattu"
#: editor/plugins/script_text_editor.cpp
@@ -5019,11 +5003,11 @@ msgstr "Lisää tai poista väriluiskalta"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Curve Map"
-msgstr ""
+msgstr "Lisää tai poista käyräkartalta"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Modify Curve Map"
-msgstr ""
+msgstr "Muokkaa käyräkarttaa"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Input Name"
@@ -5031,27 +5015,27 @@ msgstr "Vaihda syötteen nimi"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Connect Graph Nodes"
-msgstr "Yhdistä graafin nodet"
+msgstr "Yhdistä graafin solmut"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Disconnect Graph Nodes"
-msgstr "Erota graafin nodet"
+msgstr "Erota graafin solmut"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Remove Shader Graph Node"
-msgstr ""
+msgstr "Poista sävytingraafin solmu"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Move Shader Graph Node"
-msgstr ""
+msgstr "Siirrä sävytingraafin solmua"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Duplicate Graph Node(s)"
-msgstr "Kahdenna graafin node(t)"
+msgstr "Kahdenna graafin solmut(t)"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Delete Shader Graph Node(s)"
-msgstr ""
+msgstr "Poista sävytingraafin solmuja"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Error: Cyclic Connection Link"
@@ -5063,7 +5047,7 @@ msgstr "Virhe: syöteliitännät puuttuvat"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add Shader Graph Node"
-msgstr ""
+msgstr "Lisää sävytingraafin solmu"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orthogonal"
@@ -5107,7 +5091,7 @@ msgstr "Kierto %s astetta."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Keying is disabled (no key inserted)."
-msgstr ""
+msgstr "Animaation avainnus on pois päältä (avainta ei lisätty)."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Animation Key Inserted."
@@ -5115,7 +5099,7 @@ msgstr "Animaatioavain lisätty."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Objects Drawn"
-msgstr "Kappaleita piirretty"
+msgstr "Objekteja piirretty"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Material Changes"
@@ -5199,7 +5183,7 @@ msgstr "Isäntää, jonka alle ilmentymä luodaan, ei ole valittu."
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "This operation requires a single selected node."
-msgstr "Tämä toiminto vaatii yhden valitun noden."
+msgstr "Tämä toiminto vaatii yhden valitun solmun."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Normal"
@@ -5275,12 +5259,11 @@ msgstr "Liikkumisen nopeussäädin"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
-msgstr ""
+msgstr "XForm-ikkuna"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Select Mode (Q)"
-msgstr "Valitse tila"
+msgstr "Valintatila (Q)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid ""
@@ -5288,6 +5271,9 @@ msgid ""
"Alt+Drag: Move\n"
"Alt+RMB: Depth list selection"
msgstr ""
+"Vedä: Kierrä\n"
+"Alt + Vedä: Siirrä\n"
+"Alt + Hiiren oikea painike: Syvyyslistan valinta"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
@@ -5306,9 +5292,8 @@ msgid "Local Coords"
msgstr "Paikalliset koordinaatit"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Local Space Mode (%s)"
-msgstr "Skaalaustila (R)"
+msgstr "Paikallisavaruuden tila (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Mode (%s)"
@@ -5552,23 +5537,20 @@ msgid "Move (After)"
msgstr "Siirrä (jälkeen)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "SpriteFrames"
-msgstr "Pinoa Framet"
+msgstr "SpriteFrames"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox Preview:"
-msgstr "StyleBox:in esikatselu:"
+msgstr "StyleBoxin esikatselu:"
#: editor/plugins/style_box_editor_plugin.cpp
-#, fuzzy
msgid "StyleBox"
-msgstr "Tyyli"
+msgstr "StyleBox"
#: editor/plugins/texture_region_editor_plugin.cpp
-#, fuzzy
msgid "Set Region Rect"
-msgstr "Tekstuurialue"
+msgstr "Aseta alueen suorakulmio"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Snap Mode:"
@@ -5618,7 +5600,6 @@ msgid "Can't save theme to file:"
msgstr "Teemaa ei voi tallentaa tiedostoon:"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Add All Items"
msgstr "Lisää kaikki"
@@ -5632,9 +5613,8 @@ msgid "Remove Item"
msgstr "Poista"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Remove All Items"
-msgstr "Poista valitut"
+msgstr "Poista kaikki"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Remove All"
@@ -5646,7 +5626,7 @@ msgstr "Muokkaa teemaa..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
-msgstr "Teeman muokkaus."
+msgstr "Teeman muokkausvalikko."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add Class Items"
@@ -5670,15 +5650,15 @@ msgstr "Luo nykyisestä editorin teemasta"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
-msgstr ""
+msgstr "Valintaruudun valinta 1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio2"
-msgstr ""
+msgstr "Valintaruudun valinta 2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Item"
-msgstr ""
+msgstr "Osanen"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Check Item"
@@ -5689,32 +5669,28 @@ msgid "Checked Item"
msgstr "Valittu"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Lisää"
+msgstr "Valintapainike"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Checked Radio Item"
-msgstr "Valittu"
+msgstr "Valittu valintapainike"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "On"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Many"
-msgstr "Moni(a)/Monta"
+msgstr "Useita"
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
-msgstr "Asetukset"
+msgstr "Asetuksia"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "On,Monia,Useita,Asetuksia"
+msgstr "On,Useita,Asetuksia"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5753,9 +5729,8 @@ msgid "Theme"
msgstr "Teema"
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Erase Selection"
-msgstr "Framen valinta"
+msgstr "Tyhjennä valittu alue"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint TileMap"
@@ -5783,7 +5758,7 @@ msgstr "Tyhjennä valinta"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Find tile"
-msgstr "Etsi tile"
+msgstr "Etsi ruutu"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Transpose"
@@ -5798,13 +5773,12 @@ msgid "Mirror Y"
msgstr "Peilaa Y"
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Paint Tile"
-msgstr "Poimi tile"
+msgstr "Maalaa ruutu"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Pick Tile"
-msgstr "Poimi tile"
+msgstr "Poimi ruutu"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 0 degrees"
@@ -5824,7 +5798,7 @@ msgstr "Käännä 270 astetta"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Could not find tile:"
-msgstr "Tileä ei löytynyt:"
+msgstr "Ruutua ei löytynyt:"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Item name or ID:"
@@ -5839,9 +5813,8 @@ msgid "Merge from scene?"
msgstr "Yhdistä skenestä?"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Tile Set"
-msgstr "Vie tileset"
+msgstr "Ruutuvalikoima"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -5856,15 +5829,16 @@ msgid "Error"
msgstr "Virhe"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Autotiles"
-msgstr "Jaa automaattisesti"
+msgstr "Automaattiruudutus"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid ""
"Select sub-tile to use as icon, this will be also used on invalid autotile "
"bindings."
msgstr ""
+"Valitse aliruutu, jota käytetään ikonina ja myös virheellisten "
+"automaattiruudutusten ilmaisemiseen."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid ""
@@ -5875,13 +5849,12 @@ msgstr ""
"Hiiren oikea: aseta bitti pois päältä."
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select current edited sub-tile."
-msgstr "Tallenna tällä hetkellä muokattu resurssi."
+msgstr "Valitse muokattavana oleva aliruutu."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select sub-tile to change its priority."
-msgstr ""
+msgstr "Valitse aliruutu muuttaaksesi sen tärkeyttä."
#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
msgid "Cancel"
@@ -5921,7 +5894,7 @@ msgstr "Vie kaikki projektin resurssit"
#: editor/project_export.cpp
msgid "Export selected scenes (and dependencies)"
-msgstr "Vie valitut Scenet (ja riippuvuudet)"
+msgstr "Vie valitut skenet (ja riippuvuudet)"
#: editor/project_export.cpp
msgid "Export selected resources (and dependencies)"
@@ -6002,9 +5975,8 @@ msgid "Imported Project"
msgstr "Tuotu projekti"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Projektin nimi:"
+msgstr "Virheellinen projektin nimi."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6023,21 +5995,20 @@ msgid "Invalid project path (changed anything?)."
msgstr "Virheellinen projektin polku (muuttuiko mikään?)."
#: editor/project_manager.cpp
-#, fuzzy
msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
-msgstr "Ei voitu luoda godot.cfg -tiedostoa projektin polkuun."
+msgstr ""
+"Tiedoston project.godot lataus projektin polusta epäonnistui (virhe %d). Se "
+"saattaa puuttua tai olla vioittunut."
#: editor/project_manager.cpp
-#, fuzzy
msgid "Couldn't edit project.godot in project path."
-msgstr "Ei voitu luoda godot.cfg -tiedostoa projektin polkuun."
+msgstr "Ei voitu muokata project.godot tiedostoa projektin polussa."
#: editor/project_manager.cpp
-#, fuzzy
msgid "Couldn't create project.godot in project path."
-msgstr "Ei voitu luoda godot.cfg -tiedostoa projektin polkuun."
+msgstr "Tiedoston project.godot luonti projektin polkuun epäonnistui."
#: editor/project_manager.cpp
msgid "The following files failed extraction from package:"
@@ -6109,9 +6080,9 @@ msgid ""
"Please edit the project and set the main scene in \"Project Settings\" under "
"the \"Application\" category."
msgstr ""
-"Projektia ei voida suorittaa: pääsceneä ei ole määritetty.\n"
-"Ole hyvä ja muokkaa projektia ja aseta pääscene projektin asetuksista "
-"\"Application\" -kategoriasta."
+"Projektia ei voida suorittaa: pääskeneä ei ole määritetty.\n"
+"Ole hyvä ja muokkaa projektia ja aseta pääskene projektin asetuksista "
+"\"Application\"-kategoriasta."
#: editor/project_manager.cpp
msgid ""
@@ -6205,13 +6176,12 @@ msgid "Mouse Button"
msgstr "Hiiren painike"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Virheellinen toiminnon nimi. Se ei voi olla tyhjä eikä voi sisältää merkkejä "
-"'/', ':', '=', '\\' tai '\"'"
+"'/', ':', '=', '\\' tai '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6219,11 +6189,11 @@ msgstr "Tapahtuma '%s' on jo olemassa!"
#: editor/project_settings_editor.cpp
msgid "Rename Input Action Event"
-msgstr "Nimeä syöttötapahtuma uudelleen"
+msgstr "Nimeä syötetoiminto uudelleen"
#: editor/project_settings_editor.cpp
msgid "Add Input Action Event"
-msgstr "Lisää syöttötapahtuma"
+msgstr "Lisää syötetoiminnon tapahtuma"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "Shift+"
@@ -6294,13 +6264,12 @@ msgid "Joypad Button Index:"
msgstr "Ohjaimen painikkeen indeksi:"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Erase Input Action"
-msgstr "Tyhjennä syöttötapahtuma"
+msgstr "Tyhjennä syötetoiminto"
#: editor/project_settings_editor.cpp
msgid "Erase Input Action Event"
-msgstr "Tyhjennä syöttötapahtuma"
+msgstr "Tyhjennä syötetoiminnon tapahtuma"
#: editor/project_settings_editor.cpp
msgid "Add Event"
@@ -6360,7 +6329,7 @@ msgstr "On jo olemassa"
#: editor/project_settings_editor.cpp
msgid "Add Input Action"
-msgstr "Lisää syöttötapahtuma"
+msgstr "Lisää syötetapahtuma"
#: editor/project_settings_editor.cpp
msgid "Error saving settings."
@@ -6416,7 +6385,7 @@ msgstr "Projektin asetukset (project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr "Yleinen"
+msgstr "Yleistä"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
@@ -6468,7 +6437,7 @@ msgstr "Korvaavuudet kielikohtaisesti:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "Kieli"
+msgstr "Kielialue"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
@@ -6492,7 +6461,7 @@ msgstr "Kielet:"
#: editor/project_settings_editor.cpp
msgid "AutoLoad"
-msgstr "Lataa automaattisesti"
+msgstr "Automaattilataus"
#: editor/property_editor.cpp
msgid "Pick a Viewport"
@@ -6512,11 +6481,11 @@ msgstr "Nolla"
#: editor/property_editor.cpp
msgid "Easing In-Out"
-msgstr ""
+msgstr "Helpotus sisään-ulos"
#: editor/property_editor.cpp
msgid "Easing Out-In"
-msgstr ""
+msgstr "Helpotus ulos-sisään"
#: editor/property_editor.cpp
msgid "File..."
@@ -6532,7 +6501,7 @@ msgstr "Aseta"
#: editor/property_editor.cpp
msgid "Select Node"
-msgstr "Valitse node"
+msgstr "Valitse solmu"
#: editor/property_editor.cpp
msgid "New Script"
@@ -6544,7 +6513,7 @@ msgstr "Uusi %s"
#: editor/property_editor.cpp
msgid "Make Unique"
-msgstr "Tee ainutkertaiseksi"
+msgstr "Tee yksilölliseksi"
#: editor/property_editor.cpp
msgid "Show in File System"
@@ -6559,13 +6528,12 @@ msgid "Error loading file: Not a resource!"
msgstr "Virhe ladattaessa tiedostoa: Ei ole resurssi!"
#: editor/property_editor.cpp
-#, fuzzy
msgid "Selected node is not a Viewport!"
-msgstr "Valitse tuotava(t) node(t)"
+msgstr "Valittu solmu ei ole Viewport!"
#: editor/property_editor.cpp
msgid "Pick a Node"
-msgstr "Poimi node"
+msgstr "Poimi solmu"
#: editor/property_editor.cpp
msgid "Bit %d, val %d."
@@ -6609,7 +6577,7 @@ msgstr "Muunnettua kuva ei voitu ladata takaisin PVRTC-työkalulla:"
#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
msgid "Reparent Node"
-msgstr "Vaihda noden isäntää"
+msgstr "Vaihda solmun isäntää"
#: editor/reparent_dialog.cpp
msgid "Reparent Location (Select new Parent):"
@@ -6641,7 +6609,7 @@ msgstr "Pääskenen argumentit:"
#: editor/run_settings_dialog.cpp
msgid "Scene Run Settings"
-msgstr "Scenen suorittamisasetukset"
+msgstr "Skenen suorittamisasetukset"
#: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp
#: scene/gui/dialogs.cpp
@@ -6650,7 +6618,7 @@ msgstr "OK"
#: editor/scene_tree_dock.cpp
msgid "No parent to instance the scenes at."
-msgstr "Nodea, jonka alle skenen ilmentymä luodaan, ei ole valittu."
+msgstr "Solmua, jonka alle skenen ilmentymä luodaan, ei ole valittu."
#: editor/scene_tree_dock.cpp
msgid "Error loading scene from %s"
@@ -6662,7 +6630,7 @@ msgid ""
"of its nodes."
msgstr ""
"Skenestä '%s' ei voida luoda ilmentymää, koska nykyinen skene on olemassa "
-"jossakin sen nodeista."
+"jossakin sen solmuista."
#: editor/scene_tree_dock.cpp
msgid "Instance Scene(s)"
@@ -6674,23 +6642,23 @@ msgstr "Tätä toimenpidettä ei voi tehdä puun juurelle."
#: editor/scene_tree_dock.cpp
msgid "Move Node In Parent"
-msgstr "Siirrä node isännän alle"
+msgstr "Siirrä solmu isännän alle"
#: editor/scene_tree_dock.cpp
msgid "Move Nodes In Parent"
-msgstr "Siirrä nodet isännän alle"
+msgstr "Siirrä solmut isännän alle"
#: editor/scene_tree_dock.cpp
msgid "Duplicate Node(s)"
-msgstr "Monista node(t)"
+msgstr "Kahdenna solmu(t)"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)?"
-msgstr "Poista Node(t)?"
+msgstr "Poista solmu(t)?"
#: editor/scene_tree_dock.cpp
msgid "Can not perform with the root node."
-msgstr "Ei voi tehdä juurinodelle."
+msgstr "Ei voi tehdä juurisolmulle."
#: editor/scene_tree_dock.cpp
msgid "This operation can't be done on instanced scenes."
@@ -6698,11 +6666,11 @@ msgstr "Tätä toimintoa ei voi tehdä skenejen ilmentymille."
#: editor/scene_tree_dock.cpp
msgid "Save New Scene As..."
-msgstr "Tallenna uusi scene nimellä..."
+msgstr "Tallenna uusi skene nimellä..."
#: editor/scene_tree_dock.cpp
msgid "Editable Children"
-msgstr "Muokattavat alinodet"
+msgstr "Muokattavat alisolmut"
#: editor/scene_tree_dock.cpp
msgid "Load As Placeholder"
@@ -6718,15 +6686,15 @@ msgstr "Käy järkeen!"
#: editor/scene_tree_dock.cpp
msgid "Can't operate on nodes from a foreign scene!"
-msgstr "Ei voida käyttää ulkopuolisen scenen nodeja!"
+msgstr "Ei voida käyttää ulkopuolisen skenen solmuja!"
#: editor/scene_tree_dock.cpp
msgid "Can't operate on nodes the current scene inherits from!"
-msgstr "Ei voida käyttää nodeja, jotka periytyvät nykyisestä scenestä!"
+msgstr "Ei voida käyttää solmuja, joista nykyinen skene periytyy!"
#: editor/scene_tree_dock.cpp
msgid "Remove Node(s)"
-msgstr "Poista Node(t)"
+msgstr "Poista solmu(t)"
#: editor/scene_tree_dock.cpp
msgid ""
@@ -6738,7 +6706,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Error saving scene."
-msgstr "Virhe tallennettaessa sceneä."
+msgstr "Virhe tallennettaessa skeneä."
#: editor/scene_tree_dock.cpp
msgid "Error duplicating scene to save it."
@@ -6754,11 +6722,11 @@ msgstr "Poista perintä"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)"
-msgstr "Poista Node(t)"
+msgstr "Poista solmu(t)"
#: editor/scene_tree_dock.cpp
msgid "Add Child Node"
-msgstr "Lisää lapsinode"
+msgstr "Lisää alisolmu"
#: editor/scene_tree_dock.cpp
msgid "Instance Child Scene"
@@ -6778,7 +6746,7 @@ msgstr "Tyhjennä skripti"
#: editor/scene_tree_dock.cpp
msgid "Merge From Scene"
-msgstr "Yhdistä scenestä"
+msgstr "Yhdistä skenestä"
#: editor/scene_tree_dock.cpp
msgid "Save Branch as Scene"
@@ -6786,7 +6754,7 @@ msgstr "Tallenna haara skenenä"
#: editor/scene_tree_dock.cpp
msgid "Copy Node Path"
-msgstr "Kopioi Noden polku"
+msgstr "Kopioi solmun polku"
#: editor/scene_tree_dock.cpp
msgid "Delete (No Confirm)"
@@ -6794,32 +6762,31 @@ msgstr "Poista (ei varmistusta)"
#: editor/scene_tree_dock.cpp
msgid "Add/Create a New Node"
-msgstr "Lisää/Luo uusi Node"
+msgstr "Lisää/Luo uusi solmu"
#: editor/scene_tree_dock.cpp
msgid ""
"Instance a scene file as a Node. Creates an inherited scene if no root node "
"exists."
msgstr ""
-"Luo skenetiedostosta ilmentymän nodeksi. Luo periytetyn skenen jos "
-"juurinodea ei ole olemassa."
+"Luo skenetiedostosta ilmentymän solmuksi. Luo periytetyn skenen jos "
+"juurisolmua ei ole olemassa."
#: editor/scene_tree_dock.cpp
msgid "Filter nodes"
-msgstr "Suodata nodeja"
+msgstr "Suodata solmuja"
#: editor/scene_tree_dock.cpp
msgid "Attach a new or existing script for the selected node."
-msgstr "Liitä uusi tai olemassa oleva skripti valitulle nodelle."
+msgstr "Liitä uusi tai olemassa oleva skripti valitulle solmulle."
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
-msgstr "Poista skripti valitulta nodelta."
+msgstr "Poista skripti valitulta solmulta."
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Remote"
-msgstr "Poista"
+msgstr "Etäinen"
#: editor/scene_tree_dock.cpp
msgid "Local"
@@ -6835,22 +6802,22 @@ msgstr "Tyhjennä!"
#: editor/scene_tree_editor.cpp
msgid "Toggle Spatial Visible"
-msgstr ""
+msgstr "Aseta Spatial näkyvyys päälle/pois"
#: editor/scene_tree_editor.cpp
msgid "Toggle CanvasItem Visible"
-msgstr ""
+msgstr "Aseta CanvasItem näkyvyys päälle/pois"
#: editor/scene_tree_editor.cpp
msgid "Node configuration warning:"
-msgstr "Noden konfiguroinnin varoitus:"
+msgstr "Solmun konfiguroinnin varoitus:"
#: editor/scene_tree_editor.cpp
msgid ""
"Node has connection(s) and group(s)\n"
"Click to show signals dock."
msgstr ""
-"Nodella on liitäntöjä ja ryhmiä\n"
+"Solmulla on liitäntöjä ja ryhmiä\n"
"Napsauta näyttääksesi signaalitelakan."
#: editor/scene_tree_editor.cpp
@@ -6858,7 +6825,7 @@ msgid ""
"Node has connections.\n"
"Click to show signals dock."
msgstr ""
-"Nodella on liitäntöjä.\n"
+"Solmulla on liitäntöjä.\n"
"Napsauta näyttääksesi signaalitelakan."
#: editor/scene_tree_editor.cpp
@@ -6866,7 +6833,7 @@ msgid ""
"Node is in group(s).\n"
"Click to show groups dock."
msgstr ""
-"Node kuuluu ryhmään.\n"
+"Solmu kuuluu ryhmään.\n"
"Napsauta näyttääksesi ryhmätelakan."
#: editor/scene_tree_editor.cpp
@@ -6878,7 +6845,7 @@ msgid ""
"Node is locked.\n"
"Click to unlock"
msgstr ""
-"Node on lukittu.\n"
+"Solmu on lukittu.\n"
"Napsauta lukituksen avaamiseksi"
#: editor/scene_tree_editor.cpp
@@ -6886,7 +6853,7 @@ msgid ""
"Children are not selectable.\n"
"Click to make selectable"
msgstr ""
-"Alinodet eivät ole valittavissa.\n"
+"Alisolmut eivät ole valittavissa.\n"
"Napsauta niiden tekemiseksi valittavaksi"
#: editor/scene_tree_editor.cpp
@@ -6895,23 +6862,23 @@ msgstr "Aseta näkyvyys"
#: editor/scene_tree_editor.cpp
msgid "Invalid node name, the following characters are not allowed:"
-msgstr "Virheellinen noden nimi, seuraavat merkit eivät ole sallittuja:"
+msgstr "Virheellinen solmun nimi, seuraavat merkit eivät ole sallittuja:"
#: editor/scene_tree_editor.cpp
msgid "Rename Node"
-msgstr "Nimeä Node uudelleen"
+msgstr "Nimeä solmu uudelleen"
#: editor/scene_tree_editor.cpp
msgid "Scene Tree (Nodes):"
-msgstr "Skenepuu (nodet):"
+msgstr "Skenepuu (solmut):"
#: editor/scene_tree_editor.cpp
msgid "Node Configuration Warning!"
-msgstr "Noden konfigurointivaroitus!"
+msgstr "Solmun konfigurointivaroitus!"
#: editor/scene_tree_editor.cpp
msgid "Select a Node"
-msgstr "Valitse Node"
+msgstr "Valitse solmu"
#: editor/script_create_dialog.cpp
msgid "Error loading template '%s'"
@@ -7011,7 +6978,7 @@ msgstr "Sisäänrakennettu skripti"
#: editor/script_create_dialog.cpp
msgid "Attach Node Script"
-msgstr "Liitä Noden skripti"
+msgstr "Liitä solmun skripti"
#: editor/script_editor_debugger.cpp
msgid "Remote "
@@ -7155,7 +7122,7 @@ msgstr "Muuta valon sädettä"
#: editor/spatial_editor_gizmos.cpp
msgid "Change AudioStreamPlayer3D Emission Angle"
-msgstr "Muuta AudioStreamPlayer3D noden suuntausta"
+msgstr "Muuta AudioStreamPlayer3D solmun suuntausta"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Camera FOV"
@@ -7195,7 +7162,7 @@ msgstr "Muuta partikkelien AABB"
#: editor/spatial_editor_gizmos.cpp
msgid "Change Probe Extents"
-msgstr ""
+msgstr "Muuta Proben ulottuvuuksia"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Select the dynamic library for this entry"
@@ -7230,7 +7197,6 @@ msgid "Add an architecture entry"
msgstr "Lisää arkkitehtuurikohde"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "GDNativeLibrary"
msgstr "GDNativeLibrary"
@@ -7247,7 +7213,6 @@ msgid "Libraries: "
msgstr "Kirjastot: "
#: modules/gdnative/register_types.cpp
-#, fuzzy
msgid "GDNative"
msgstr "GDNative"
@@ -7335,18 +7300,16 @@ msgid "GridMap Duplicate Selection"
msgstr "Kahdenna valinta"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Grid Map"
msgstr "Ruudukko"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Snap View"
-msgstr "Huippunäkymä"
+msgstr "Tartu näkymään"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clip Disabled"
-msgstr "Leikkaus poistettu käytöstä"
+msgstr "Leikkaus pois käytöstä"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clip Above"
@@ -7369,35 +7332,32 @@ msgid "Edit Z Axis"
msgstr "Muokkaa Z-akselia"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Cursor Rotate X"
-msgstr "Ctrl: Pyöritä/kierrä"
+msgstr "Kierrä kohdistinta X-akselilla"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Cursor Rotate Y"
-msgstr "Ctrl: Pyöritä/kierrä"
+msgstr "Kierrä kohdistinta Y-akselilla"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Cursor Rotate Z"
-msgstr "Ctrl: Pyöritä/kierrä"
+msgstr "Kierrä kohdistinta Z-akselilla"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Back Rotate X"
-msgstr ""
+msgstr "Kierrä kohdistinta X-akselilla takaperin"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Back Rotate Y"
-msgstr ""
+msgstr "Kierrä kohdistinta Y-akselilla takaperin"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Back Rotate Z"
-msgstr ""
+msgstr "Kierrä kohdistinta Z-akselilla takaperin"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Clear Rotation"
-msgstr ""
+msgstr "Poista kohdistimen kierto"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Create Area"
@@ -7416,13 +7376,12 @@ msgid "Clear Selection"
msgstr "Tyhjennä valinta"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Settings"
-msgstr "Näyttöruudun asetukset"
+msgstr "Ruudukon asetukset"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Pick Distance:"
-msgstr "Poimi etäisyys:"
+msgstr "Poimintaetäisyys:"
#: modules/mono/csharp_script.cpp
msgid "Class name can't be a reserved keyword"
@@ -7485,12 +7444,16 @@ msgid ""
"A node yielded without working memory, please read the docs on how to yield "
"properly!"
msgstr ""
+"Solmu väisti ilman työmuistia, ole hyvä ja lue dokumentaatiosta kuinka "
+"väistö (yield) on tehtävä!"
#: modules/visual_script/visual_script.cpp
msgid ""
"Node yielded, but did not return a function state in the first working "
"memory."
msgstr ""
+"Solmu väisti (yield), mutta ei palauttanut funktion tilaa ensimmäiselle "
+"työmuistille."
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7498,15 +7461,15 @@ msgid ""
"your node please."
msgstr ""
"Paluuarvo täytyy sijoittaa työmuistin ensimmäiselle elementille! Ole hyvä ja "
-"korjaa nodesi."
+"korjaa solmusi."
#: modules/visual_script/visual_script.cpp
msgid "Node returned an invalid sequence output: "
-msgstr ""
+msgstr "Solmu palautti virheellisen jakson tulosteen: "
#: modules/visual_script/visual_script.cpp
msgid "Found sequence bit but not the node in the stack, report bug!"
-msgstr ""
+msgstr "Jaksobitti löytyi, mutta solmua ei löydy pinosta, raportoi bugi!"
#: modules/visual_script/visual_script.cpp
msgid "Stack overflow with stack depth: "
@@ -7578,47 +7541,52 @@ msgstr "Vaihda lauseketta"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node"
-msgstr "Lisää Node"
+msgstr "Lisää solmu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove VisualScript Nodes"
-msgstr "Poista VisualScript nodet"
+msgstr "Poista VisualScript solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Duplicate VisualScript Nodes"
-msgstr "Kahdenna VisualScript nodet"
+msgstr "Kahdenna VisualScript solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature."
msgstr ""
+"Pidä %s pohjassa pudottaaksesi Getterin. Pidä Shift pohjassa pudottaaksesi "
+"yleisen tunnisteen."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."
msgstr ""
+"Pidä Ctrl pohjassa pudottaaksesi Getterin. Pidä Shift pohjassa pudottaaksesi "
+"yleisen tunnisteen."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold %s to drop a simple reference to the node."
-msgstr ""
+msgstr "Pidä %s pohjassa pudottaaksesi yksinkertaisen viittauksen solmuun."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold Ctrl to drop a simple reference to the node."
-msgstr ""
+msgstr "Pidä Ctrl pohjassa pudottaaksesi yksinkertaisen viittauksen solmuun."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold %s to drop a Variable Setter."
-msgstr ""
+msgstr "Pidä %s pohjassa pudottaaksesi muuttujan asettajan (Variable Setter)."
#: modules/visual_script/visual_script_editor.cpp
msgid "Hold Ctrl to drop a Variable Setter."
msgstr ""
+"Pidä Ctrl pohjassa pudottaaksesi muuttujan asettajan (Variable Setter)."
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Preload Node"
-msgstr "Lisää esiladattu node"
+msgstr "Lisää esiladattu solmu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node(s) From Tree"
-msgstr "Lisää Nodet puusta"
+msgstr "Lisää solmut puusta"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Getter Property"
@@ -7634,15 +7602,15 @@ msgstr "Muuta kantatyyppiä"
#: modules/visual_script/visual_script_editor.cpp
msgid "Move Node(s)"
-msgstr "Siirrä node(t)"
+msgstr "Siirrä solmu(t)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove VisualScript Node"
-msgstr "Poista VisualScript node"
+msgstr "Poista VisualScript solmu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Connect Nodes"
-msgstr "Kytke nodet"
+msgstr "Kytke solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Condition"
@@ -7654,15 +7622,15 @@ msgstr "Sarja"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
-msgstr ""
+msgstr "Valinta (Switch)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
-msgstr ""
+msgstr "Iteraattori"
#: modules/visual_script/visual_script_editor.cpp
msgid "While"
-msgstr ""
+msgstr "Kun (While)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Return"
@@ -7674,7 +7642,7 @@ msgstr "Kutsu"
#: modules/visual_script/visual_script_editor.cpp
msgid "Get"
-msgstr ""
+msgstr "Get"
#: modules/visual_script/visual_script_editor.cpp
msgid "Script already has function '%s'"
@@ -7686,7 +7654,7 @@ msgstr "Vaihda syötteen arvo"
#: modules/visual_script/visual_script_editor.cpp
msgid "Can't copy the function node."
-msgstr "Ei voida kopioida funktionodea."
+msgstr "Ei voida kopioida funktiosolmua."
#: modules/visual_script/visual_script_editor.cpp
msgid "Clipboard is empty!"
@@ -7694,7 +7662,7 @@ msgstr "Leikepöytä on tyhjä!"
#: modules/visual_script/visual_script_editor.cpp
msgid "Paste VisualScript Nodes"
-msgstr "Liitä VisualScript nodet"
+msgstr "Liitä VisualScript solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Remove Function"
@@ -7730,7 +7698,7 @@ msgstr "Kantatyyppi:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Available Nodes:"
-msgstr "Saatavilla olevat Nodet:"
+msgstr "Saatavilla olevat solmut:"
#: modules/visual_script/visual_script_editor.cpp
msgid "Select or create a function to edit graph"
@@ -7750,19 +7718,19 @@ msgstr "Poista valitut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Find Node Type"
-msgstr "Etsi Noden tyyppi"
+msgstr "Etsi solmun tyyppi"
#: modules/visual_script/visual_script_editor.cpp
msgid "Copy Nodes"
-msgstr "Kopioi Nodet"
+msgstr "Kopioi solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Cut Nodes"
-msgstr "Leikkaa Nodet"
+msgstr "Leikkaa solmut"
#: modules/visual_script/visual_script_editor.cpp
msgid "Paste Nodes"
-msgstr "Liitä Nodet"
+msgstr "Liitä solmut"
#: modules/visual_script/visual_script_flow_control.cpp
msgid "Input type not iterable: "
@@ -7782,15 +7750,15 @@ msgstr "Virheellinen osoitinominaisuuden nimi."
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Base object is not a Node!"
-msgstr "Kantaobjekti ei ole Node!"
+msgstr "Kantaobjekti ei ole solmu!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Path does not lead Node!"
-msgstr "Polku ei vie Nodeen!"
+msgstr "Polku ei johda solmuun!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name '%s' in node %s."
-msgstr "Virheellinen osoitinominaisuuden nimi '%s' nodessa %s."
+msgstr "Virheellinen osoitinominaisuuden nimi '%s' solmussa %s."
#: modules/visual_script/visual_script_nodes.cpp
msgid ": Invalid argument of type: "
@@ -7811,7 +7779,7 @@ msgstr "VariableSet ei löytynyt skriptistä: "
#: modules/visual_script/visual_script_nodes.cpp
msgid "Custom node has no _step() method, can't process graph."
msgstr ""
-"Mukautetulla nodella ei ole _step() metodia, graafia ei voida käsitellä."
+"Mukautetulla solmulla ei ole _step() metodia, graafia ei voida käsitellä."
#: modules/visual_script/visual_script_nodes.cpp
msgid ""
@@ -7875,10 +7843,10 @@ msgid ""
"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
"define its shape."
msgstr ""
-"Tämän noden alaisuudessa ei ole muotoja, joten se ei voi olla "
+"Tämän solmun alaisuudessa ei ole muotoja, joten se ei voi olla "
"vuorovaikutuksessa avaruuden kanssa.\n"
-"Harkitse CollisionShape2D tai CollisionPolygon2D noden lisäämistä alinodeksi "
-"muodon määrittämiseksi."
+"Harkitse CollisionShape2D tai CollisionPolygon2D solmun lisäämistä "
+"alisolmuksi muodon määrittämiseksi."
#: scene/2d/collision_polygon_2d.cpp
msgid ""
@@ -7887,12 +7855,12 @@ msgid ""
"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
msgstr ""
"CollisionPolygon2D toimii törmäysmuotona ainoastaan CollisionObject2D "
-"nodesta perityille nodeille. Käytä sitä ainoastaan Area2D, StaticBody2D, "
+"solmusta perityille solmuille. Käytä sitä ainoastaan Area2D, StaticBody2D, "
"RigidBody2D, KinematicBody2D, jne. alla antaaksesi niille muodon."
#: scene/2d/collision_polygon_2d.cpp
msgid "An empty CollisionPolygon2D has no effect on collision."
-msgstr "Tyhjällä CollisionPolygon2D:llä ei ole vaikutusta törmäyksessä."
+msgstr "Tyhjällä CollisionPolygon2D solmulla ei ole vaikutusta törmäyksessä."
#: scene/2d/collision_shape_2d.cpp
msgid ""
@@ -7900,8 +7868,8 @@ msgid ""
"CollisionObject2D derived node. Please only use it as a child of Area2D, "
"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
msgstr ""
-"CollisionShape2D toimii törmäysmuotona ainoastaan CollisionObject2D nodesta "
-"perityille nodeille. Käytä sitä ainoastaan Area2D, StaticBody2D, "
+"CollisionShape2D toimii törmäysmuotona ainoastaan CollisionObject2D solmusta "
+"perityille solmuille. Käytä sitä ainoastaan Area2D, StaticBody2D, "
"RigidBody2D, KinematicBody2D, jne. alla antaaksesi niille muodon."
#: scene/2d/collision_shape_2d.cpp
@@ -7909,7 +7877,7 @@ msgid ""
"A shape must be provided for CollisionShape2D to function. Please create a "
"shape resource for it!"
msgstr ""
-"CollisionShape2D nodella täytyy olla muoto, jotta se toimisi. Ole hyvä ja "
+"CollisionShape2D solmulla täytyy olla muoto, jotta se toimisi. Ole hyvä ja "
"luo sille muotoresurssi!"
#: scene/2d/light_2d.cpp
@@ -7937,22 +7905,22 @@ msgid ""
"A NavigationPolygon resource must be set or created for this node to work. "
"Please set a property or draw a polygon."
msgstr ""
-"Tälle nodelle on asetettava tai luotava NavigationPolygon resurssi, jotta se "
-"toimisi. Ole hyvä ja aseta ominaisuus tai piirrä monikulmio."
+"Tälle solmulle on asetettava tai luotava NavigationPolygon resurssi, jotta "
+"se toimisi. Ole hyvä ja aseta ominaisuus tai piirrä monikulmio."
#: scene/2d/navigation_polygon.cpp
msgid ""
"NavigationPolygonInstance must be a child or grandchild to a Navigation2D "
"node. It only provides navigation data."
msgstr ""
-"NavigationPolygonInstance noden täytyy olla Navigation2D noden alaisuudessa. "
-"Se tarjoaa vain navigointidataa."
+"NavigationPolygonInstance solmun täytyy olla Navigation2D solmun "
+"alaisuudessa. Se tarjoaa vain navigointidataa."
#: scene/2d/parallax_layer.cpp
msgid ""
"ParallaxLayer node only works when set as child of a ParallaxBackground node."
msgstr ""
-"ParallaxLayer node toimii ainoastaan, jos se on ParallaxBackground noden "
+"ParallaxLayer solmu toimii ainoastaan, jos se on ParallaxBackground solmun "
"alla."
#: scene/2d/particles_2d.cpp scene/3d/particles.cpp
@@ -7966,8 +7934,7 @@ msgstr ""
#: scene/2d/path_2d.cpp
msgid "PathFollow2D only works when set as a child of a Path2D node."
msgstr ""
-"PathFollow2D toimii ainoastaan ollessaan asetettuna Path2D Node:n "
-"lapsiolioksi."
+"PathFollow2D toimii ainoastaan ollessaan asetettuna Path2D solmun alle."
#: scene/2d/physics_body_2d.cpp
msgid ""
@@ -7975,10 +7942,14 @@ msgid ""
"by the physics engine when running.\n"
"Change the size in children collision shapes instead."
msgstr ""
+"Fysiikkamoottori ylikirjoittaa RigidBody2D kokomuutokset (hahmo- tai "
+"jäykkätila) ajon aikana.\n"
+"Muuta sen sijaan solmun alla olevia törmäysmuotoja."
#: scene/2d/remote_transform_2d.cpp
msgid "Path property must point to a valid Node2D node to work."
-msgstr "Polku täytyy olla määritetty toimivaan Node2D solmuun toimiakseen."
+msgstr ""
+"Polkuominaisuuden täytyy osoittaa kelvolliseen Node2D solmuun toimiakseen."
#: scene/2d/visibility_notifier_2d.cpp
msgid ""
@@ -7990,11 +7961,11 @@ msgstr ""
#: scene/3d/arvr_nodes.cpp
msgid "ARVRCamera must have an ARVROrigin node as its parent"
-msgstr "ARVRCamera noden isännän täytyy olla ARVROrigin"
+msgstr "ARVRCamera solmun isännän täytyy olla ARVROrigin solmu"
#: scene/3d/arvr_nodes.cpp
msgid "ARVRController must have an ARVROrigin node as its parent"
-msgstr "ARVRController noden isännän täytyy olla ARVROrigin node"
+msgstr "ARVRController solmun isännän täytyy olla ARVROrigin solmu"
#: scene/3d/arvr_nodes.cpp
msgid ""
@@ -8006,7 +7977,7 @@ msgstr ""
#: scene/3d/arvr_nodes.cpp
msgid "ARVRAnchor must have an ARVROrigin node as its parent"
-msgstr "ARVRAnchor noden isännän täytyy olla ARVROrigin node"
+msgstr "ARVRAnchor solmun isännän täytyy olla ARVROrigin solmu"
#: scene/3d/arvr_nodes.cpp
msgid ""
@@ -8018,11 +7989,11 @@ msgstr ""
#: scene/3d/arvr_nodes.cpp
msgid "ARVROrigin requires an ARVRCamera child node"
-msgstr "ARVROrigin tarvitsee ARVRCamera noden alinodeksi"
+msgstr "ARVROrigin solmu tarvitsee ARVRCamera alisolmun"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
@@ -8030,11 +8001,11 @@ msgstr "(Aikaa jäljellä: %d:%02d s)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
-msgstr ""
+msgstr "Piirretään meshejä: "
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Lights:"
-msgstr ""
+msgstr "Piirretään valoja:"
#: scene/3d/baked_lightmap.cpp scene/3d/gi_probe.cpp
msgid "Finishing Plot"
@@ -8042,7 +8013,7 @@ msgstr "Viimeistellään piirto"
#: scene/3d/baked_lightmap.cpp
msgid "Lighting Meshes: "
-msgstr ""
+msgstr "Valaistaan meshejä: "
#: scene/3d/collision_object.cpp
msgid ""
@@ -8050,10 +8021,10 @@ msgid ""
"Consider adding CollisionShape or CollisionPolygon children nodes to define "
"its shape."
msgstr ""
-"Tällä nodella ei ole alimuotoja, joten se ei voi olla vuorovaikutuksessa "
+"Tällä solmulla ei ole alimuotoja, joten se ei voi olla vuorovaikutuksessa "
"avaruuden kanssa.\n"
-"Harkitse CollisionShape tai CollisionPolygon noden lisäämistä sen alinodeksi "
-"määritelläksesi sen muodon."
+"Harkitse CollisionShape tai CollisionPolygon solmun lisäämistä sen "
+"alisolmuksi määritelläksesi sen muodon."
#: scene/3d/collision_polygon.cpp
msgid ""
@@ -8061,13 +8032,13 @@ msgid ""
"CollisionObject derived node. Please only use it as a child of Area, "
"StaticBody, RigidBody, KinematicBody, etc. to give them a shape."
msgstr ""
-"CollisionPolygon node antaa ainoastaan törmäysmuodon CollisionObject nodesta "
-"periytyville nodeille. Käytä sitä Area, StaticBody, RigidBody, "
-"KinematicBody, jne. nodejen alla antaaksesi niille muodon."
+"CollisionPolygon solmu antaa ainoastaan törmäysmuodon CollisionObject "
+"solmusta periytyville solmuille. Käytä sitä Area, StaticBody, RigidBody, "
+"KinematicBody, jne. solmujen alla antaaksesi niille muodon."
#: scene/3d/collision_polygon.cpp
msgid "An empty CollisionPolygon has no effect on collision."
-msgstr "Tyhjällä CollisionPolygon:illa ei ole vaikutusta törmäyksessä."
+msgstr "Tyhjällä CollisionPolygon solmulla ei ole vaikutusta törmäyksessä."
#: scene/3d/collision_shape.cpp
msgid ""
@@ -8075,26 +8046,26 @@ msgid ""
"derived node. Please only use it as a child of Area, StaticBody, RigidBody, "
"KinematicBody, etc. to give them a shape."
msgstr ""
-"CollisionShape node antaa ainoastaan törmäysmuodon CollisionObject nodesta "
-"periytyville nodeille. Käytä sitä Area, StaticBody, RigidBody, "
-"KinematicBody, jne. nodejen alla antaaksesi niille muodon."
+"CollisionShape solmu antaa ainoastaan törmäysmuodon CollisionObject solmusta "
+"periytyville solmuille. Käytä sitä Area, StaticBody, RigidBody, "
+"KinematicBody, jne. solmujen alla antaaksesi niille muodon."
#: scene/3d/collision_shape.cpp
msgid ""
"A shape must be provided for CollisionShape to function. Please create a "
"shape resource for it!"
msgstr ""
-"CollisionShape nodelle täytyy antaa muoto, jotta se toimisi. Ole hyvä ja luo "
-"sille muotoresurssi!"
+"CollisionShape solmulle täytyy antaa muoto, jotta se toimisi. Ole hyvä ja "
+"luo sille muotoresurssi!"
#: scene/3d/gi_probe.cpp
msgid "Plotting Meshes"
-msgstr ""
+msgstr "Piirretään meshejä"
#: scene/3d/navigation_mesh.cpp
msgid "A NavigationMesh resource must be set or created for this node to work."
msgstr ""
-"Tälle nodelle täytyy asettaa tai luoda NavigationMesh resurssi, jotta se "
+"Tälle solmulle täytyy asettaa tai luoda NavigationMesh resurssi, jotta se "
"toimisi."
#: scene/3d/navigation_mesh.cpp
@@ -8102,7 +8073,7 @@ msgid ""
"NavigationMeshInstance must be a child or grandchild to a Navigation node. "
"It only provides navigation data."
msgstr ""
-"NavigationMeshInstance noden täytyy olla Navigation noden alaisuudessa. Se "
+"NavigationMeshInstance solmun täytyy olla Navigation solmun alaisuudessa. Se "
"tarjoaa vain navigointidataa."
#: scene/3d/particles.cpp
@@ -8118,10 +8089,13 @@ msgid ""
"by the physics engine when running.\n"
"Change the size in children collision shapes instead."
msgstr ""
+"Fysiikkamoottori ylikirjoittaa RigidBody kokomuutokset (hahmo- tai "
+"jäykkätilassa) ajon aikana.\n"
+"Muuta sen sijaan solmun alla olevia törmäysmuotoja."
#: scene/3d/remote_transform.cpp
msgid "Path property must point to a valid Spatial node to work."
-msgstr "Polkuominaisuuden täytyy osoittaa Spatial nodeen toimiakseen."
+msgstr "Polkuominaisuuden täytyy osoittaa Spatial solmuun toimiakseen."
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
@@ -8148,7 +8122,7 @@ msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
"order for AnimatedSprite3D to display frames."
msgstr ""
-"AnimatedSprite3D nodelle täytyy luoda tai asettaa 'Frames' ominaisuudeksi "
+"AnimatedSprite3D solmulle täytyy luoda tai asettaa 'Frames' ominaisuudeksi "
"SpriteFrames resurssi ruutujen näyttämiseksi."
#: scene/3d/vehicle_body.cpp
@@ -8156,8 +8130,8 @@ msgid ""
"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use "
"it as a child of a VehicleBody."
msgstr ""
-"VehicleWheel node tarjoaa rengasjärjestelmän VehicleBody nodelle. Ole hyvä "
-"ja käytä sitä VehicleBody noden alla."
+"VehicleWheel solmu tarjoaa rengasjärjestelmän VehicleBody solmulle. Ole hyvä "
+"ja käytä sitä VehicleBody solmun alla."
#: scene/gui/color_picker.cpp
msgid "Raw Mode"
@@ -8218,14 +8192,14 @@ msgid ""
"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
"texture to some node for display."
msgstr ""
-"Tätä näyttöruutua ei ole asetettu renderöitäväksi. Jos haluat sen näyttävän "
-"sisältöä suoraan näytölle, tee sitä Control:in lapsi, jotta se voi saada "
-"koon. Muutoin tee siitä RenderTarget ja aseta sen sisäinen tekstuuri "
-"johonkin Nodeen näkyväksi."
+"Tätä näyttöikkunaa ei ole asetettu renderöitäväksi. Jos haluat sen näyttävän "
+"sisältöä suoraan näytölle, tee sitä Control solmun alisolmu, jotta se voi "
+"saada koon. Muutoin tee siitä RenderTarget ja aseta sen sisäinen tekstuuri "
+"johonkin solmuun näkyväksi."
#: scene/resources/dynamic_font.cpp
msgid "Error initializing FreeType."
-msgstr "Virhe FreetType:n alustamisessa."
+msgstr "Virhe FreeType:n alustamisessa."
#: scene/resources/dynamic_font.cpp
msgid "Unknown font format."
@@ -8239,6 +8213,13 @@ msgstr "Virhe fontin latauksessa."
msgid "Invalid font size."
msgstr "Virheellinen fonttikoko."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Edellinen välilehti"
+
+#~ msgid "Next"
+#~ msgstr "Seuraava"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Virheellinen tapahtuma (muut käy, paitsi '/' tai ':')."
@@ -8269,9 +8250,6 @@ msgstr "Virheellinen fonttikoko."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Ei voitu luoda godot.cfg -tiedostoa projektin polkuun."
-#~ msgid "Next"
-#~ msgstr "Seuraava"
-
#~ msgid "Not found!"
#~ msgstr "Ei löytynyt!"
diff --git a/editor/translations/fr.po b/editor/translations/fr.po
index 4c380d0b18..ee1d7b2cad 100644
--- a/editor/translations/fr.po
+++ b/editor/translations/fr.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Antoine Carrier <ac.g392@gmail.com>, 2017-2018.
# ARocherVj <a.rocher.vj@gmail.com>, 2017.
# Arthur Templé <tuturtemple@gmail.com>, 2018.
@@ -20,6 +19,7 @@
# LL <lu.lecocq@free.fr>, 2018.
# Luc Stepniewski <lior@gradstein.info>, 2017.
# Marc <marc.gilleron@gmail.com>, 2016-2017.
+# Marc-Andre Belisle <belisle.ma@gmail.com>, 2018.
# Nathan Lovato <nathan.lovato.art@gmail.com>, 2017.
# Nathan Vallet <nathanvalletmarseille@gmail.com>, 2018.
# Nicolas <flaithotw@gmail.com>, 2017.
@@ -28,6 +28,7 @@
# Nocta Senestra <nocta@net-c.com>, 2018.
# Omicron <omicron666.dev@gmail.com>, 2016, 2018.
# Onyx Steinheim <thevoxelmanonyx@gmail.com>, 2016.
+# Philippe Gervaise <blah@malvese.org>, 2018.
# Przemyslaw Gasinski <gasinski.przemek@protonmail.ch>, 2017.
# rafeu <duchainer@gmail.com>, 2016-2017.
# rawida <rawida@tempinbox.com>, 2018.
@@ -39,13 +40,12 @@
# Tommy Melançon-Roy <tommel1234@hotmail.com>, 2017-2018.
# Willow <theotimefd@aol.com>, 2018.
# Xananax <xananax@yelostudio.com>, 2017-2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-06-05 19:27+0000\n"
-"Last-Translator: Rémi Verschelde <akien@godotengine.org>\n"
+"PO-Revision-Date: 2018-06-12 16:38+0000\n"
+"Last-Translator: Philippe Gervaise <blah@malvese.org>\n"
"Language-Team: French <https://hosted.weblate.org/projects/godot-engine/"
"godot/fr/>\n"
"Language: fr\n"
@@ -53,7 +53,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 3.0\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1727,7 +1727,7 @@ msgstr "Exporter une bibliothèque de maillages"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr "Cette opération ne peut être réalisée sans noeud parent."
+msgstr "Cette opération ne peut être réalisée sans nœud racine."
#: editor/editor_node.cpp
msgid "Export Tile Set"
@@ -2572,7 +2572,7 @@ msgstr "Erreur lors de la requête de l’URL : "
#: editor/export_template_manager.cpp
msgid "Connecting to Mirror..."
-msgstr "Connexion au miroir"
+msgstr "Connexion au Miroir..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -3763,7 +3763,7 @@ msgstr "Afficher les règles"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Guides"
-msgstr "Montrer les guides"
+msgstr "Afficher les guides"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Origin"
@@ -5759,9 +5759,8 @@ msgid "Options"
msgstr "Options"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Ont,Plusieurs,Possibilités,D'options !"
+msgstr "Possède,Plusieurs,Options"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6046,9 +6045,8 @@ msgid "Imported Project"
msgstr "Projet importé"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nom du projet :"
+msgstr "Nom du Projet Invalide."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6253,13 +6251,12 @@ msgid "Mouse Button"
msgstr "Bouton de souris"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Nom d'action invalide. Il ne peux être vide ou contenir '/', ':', '=', '\\' "
-"ou '\"'"
+"ou '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8298,6 +8295,13 @@ msgstr "Erreur lors du chargement de la police."
msgid "Invalid font size."
msgstr "Taille de police invalide."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Onglet precedent"
+
+#~ msgid "Next"
+#~ msgstr "Suivant"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Action invalide (tout passe, sauf « / » ou « : »)."
@@ -8327,9 +8331,6 @@ msgstr "Taille de police invalide."
#~ msgstr ""
#~ "Impossible de trouver le fichier project.godot dans le chemin du projet."
-#~ msgid "Next"
-#~ msgstr "Suivant"
-
#~ msgid "Not found!"
#~ msgstr "Non trouvé !"
diff --git a/editor/translations/he.po b/editor/translations/he.po
index 3a86197ef5..0f1881211f 100644
--- a/editor/translations/he.po
+++ b/editor/translations/he.po
@@ -7977,12 +7977,16 @@ msgstr "שגי××” בטעינת הגופן."
msgid "Invalid font size."
msgstr "גודל הגופן שגוי."
-#~ msgid "Can't write file."
-#~ msgstr "×œ× × ×™×ª×Ÿ לכתוב קובץ."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "הלשונית הקודמת"
#~ msgid "Next"
#~ msgstr "הב×"
+#~ msgid "Can't write file."
+#~ msgstr "×œ× × ×™×ª×Ÿ לכתוב קובץ."
+
#~ msgid "Not found!"
#~ msgstr "×œ× × ×ž×¦×!"
diff --git a/editor/translations/hu.po b/editor/translations/hu.po
index 28cc68be24..b04dd073df 100644
--- a/editor/translations/hu.po
+++ b/editor/translations/hu.po
@@ -2,23 +2,22 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
+# Ãrpád Horváth <horvatha4@googlemail.com>, 2018.
# Nagy Lajos <neutron9707@gmail.com>, 2017.
# Sandor Domokos <sandor.domokos@gmail.com>, 2017-2018.
# Varga Dániel <danikah.danikah@gmail.com>, 2016-2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-04-03 06:36+0000\n"
-"Last-Translator: Sandor Domokos <sandor.domokos@gmail.com>\n"
+"PO-Revision-Date: 2018-06-17 07:39+0000\n"
+"Last-Translator: Ãrpád Horváth <horvatha4@googlemail.com>\n"
"Language-Team: Hungarian <https://hosted.weblate.org/projects/godot-engine/"
"godot/hu/>\n"
"Language: hu\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -34,7 +33,7 @@ msgstr "Animáció kulcsképkocka idő változtatás"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Animáció átmenet változtatás"
+msgstr "Animáció átmenet változtatása"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -236,7 +235,7 @@ msgstr "Animáció kulcsok nyújtás"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr "Animáció hívási nyomvonal hozzáadás"
+msgstr "Animációhoz hívási nyomvonal hozzáadása"
#: editor/animation_editor.cpp
msgid "Animation zoom."
@@ -1396,7 +1395,7 @@ msgstr "Kimenet Törlése"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Projekt export nem sikerült, hibakód %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
@@ -1646,11 +1645,11 @@ msgstr "Alap Scene megnyitás"
#: editor/editor_node.cpp
msgid "Quick Open Scene..."
-msgstr "Scene gyors megnyitás"
+msgstr "Jelenet gyors megnyitása..."
#: editor/editor_node.cpp
msgid "Quick Open Script..."
-msgstr "Szkript gyors megnyitás"
+msgstr "Szkript gyors megnyitás..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1662,7 +1661,7 @@ msgstr "Bezárás előtt menti a '%s'-n végzett módosításokat?"
#: editor/editor_node.cpp
msgid "Save Scene As..."
-msgstr "Scene mentés másként"
+msgstr "Scene mentés másként..."
#: editor/editor_node.cpp
msgid "No"
@@ -1714,7 +1713,7 @@ msgstr "Ez a művelet nem vonható vissza. Visszaállítja mindenképp?"
#: editor/editor_node.cpp
msgid "Quick Run Scene..."
-msgstr "Scene gyors futtatás"
+msgstr "Scene gyors futtatás..."
#: editor/editor_node.cpp
msgid "Quit"
@@ -1860,7 +1859,7 @@ msgstr "Hozzáad egy új jelenetet."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr "Scene"
+msgstr "Jelenet"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
@@ -1888,11 +1887,11 @@ msgstr "Új Scene"
#: editor/editor_node.cpp
msgid "New Inherited Scene..."
-msgstr "Új örökölt Scene"
+msgstr "Új örökölt Jelenet..."
#: editor/editor_node.cpp
msgid "Open Scene..."
-msgstr "Scene megnyitása"
+msgstr "Jelenet megnyitása..."
#: editor/editor_node.cpp
msgid "Save Scene"
@@ -1982,7 +1981,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr "Kis Telepítés Hálózati FR-rel"
+msgstr "Kis Telepítés Hálózati FS-sel"
#: editor/editor_node.cpp
msgid ""
@@ -2026,7 +2025,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr "Scene változtatások szinkronizálás"
+msgstr "Jelenet változtatások szinkronizálása"
#: editor/editor_node.cpp
msgid ""
@@ -2809,7 +2808,7 @@ msgstr "Scene importálás"
#: editor/import/resource_importer_scene.cpp
msgid "Importing Scene..."
-msgstr "Scene importálás"
+msgstr "Jelenet importálása..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -3430,7 +3429,7 @@ msgstr "Hivatalos"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Testing"
-msgstr "Tesztelés Alatt"
+msgstr "Tesztelés"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Assets ZIP File"
@@ -3718,14 +3717,12 @@ msgid "Show Guides"
msgstr "Vezetővonalak Megjelenítése"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Origin"
-msgstr "Rács Megjelenítése"
+msgstr "Origó Megjelenítése"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Show Viewport"
-msgstr "Segítők Megjelenítése"
+msgstr "Nézet Megjelenítése"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -4019,7 +4016,7 @@ msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "A Háló-primitív típusa nem háromszög (PRIMITIVE_TRIANGLES)!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
@@ -4965,7 +4962,7 @@ msgstr "Vec kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Scalar Operator"
-msgstr ""
+msgstr "Vektor skalár kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Operator"
@@ -4973,31 +4970,31 @@ msgstr "RGB kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Toggle Rot Only"
-msgstr ""
+msgstr "Csak vörös kapcsolása"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Function"
-msgstr ""
+msgstr "Skalár-függvény változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Function"
-msgstr ""
+msgstr "Vektor-függvény változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Uniform"
-msgstr ""
+msgstr "Egységes-skalár változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Uniform"
-msgstr ""
+msgstr "Egységes-vektor változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Uniform"
-msgstr ""
+msgstr "Egységes-RGB változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Default Value"
-msgstr ""
+msgstr "Alapérték változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change XForm Uniform"
@@ -5093,7 +5090,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Plane Transform."
-msgstr ""
+msgstr "Megnéz a Síklap transzformációját."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scaling: "
@@ -5245,7 +5242,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Doppler Enable"
-msgstr ""
+msgstr "Doppler engedélyezése"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Left"
@@ -5684,9 +5681,8 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Radio Item"
-msgstr "Elem Hozzáadása"
+msgstr "Rádió Elem"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Checked Radio Item"
@@ -5983,9 +5979,8 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Érvénytelen név."
+msgstr "Érvénytelen projektnév."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6380,7 +6375,7 @@ msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr ""
+msgstr "Ãltalános"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
@@ -7244,7 +7239,7 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Plane"
-msgstr "Következő Sík"
+msgstr "Következő Síklap"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Previous Plane"
@@ -7724,7 +7719,7 @@ msgstr ""
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Path does not lead Node!"
-msgstr ""
+msgstr "Az út nem vezeti a csomópontot!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name '%s' in node %s."
@@ -8102,6 +8097,13 @@ msgstr "Hiba a betűtípus betöltésekor."
msgid "Invalid font size."
msgstr "Érvénytelen betűtípus méret."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Előző fül"
+
+#~ msgid "Next"
+#~ msgstr "Következő"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
@@ -8112,9 +8114,6 @@ msgstr "Érvénytelen betűtípus méret."
#~ msgid "Can't write file."
#~ msgstr "Nem lehet fájlt írni."
-#~ msgid "Next"
-#~ msgstr "Következő"
-
#~ msgid "Not found!"
#~ msgstr "Nincs Találat!"
diff --git a/editor/translations/id.po b/editor/translations/id.po
index 26a9739169..3956378ce7 100644
--- a/editor/translations/id.po
+++ b/editor/translations/id.po
@@ -2,12 +2,12 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Abdul Aziz Muslim Alqudsy <abdul.aziz.muslim.alqudsy@gmail.com>, 2016.
# Andevid Dynmyn <doyan4forum@gmail.com>, 2016.
# Andinawan Asa <asaandinawan@gmail.com>, 2016.
# Damar Inderajati <damarind@gmail.com>, 2017.
# Damar S. M <the.last.walla@gmail.com>, 2017.
+# Fajar Ru <kzofajar@gmail.com>, 2018.
# Khairul Hidayat <khairulcyber4rt@gmail.com>, 2016.
# Reza Hidayat Bayu Prabowo <rh.bayu.prabowo@gmail.com>, 2018.
# Romi Kusuma Bakti <romikusumab@gmail.com>, 2017.
@@ -15,19 +15,18 @@
# Tito <ijavadroid@gmail.com>, 2018.
# Tom My <tom.asadinawan@gmail.com>, 2017.
# yursan9 <rizal.sagi@gmail.com>, 2016.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-05-31 16:39+0000\n"
-"Last-Translator: Sofyan Sugianto <sofyanartem@gmail.com>\n"
+"PO-Revision-Date: 2018-06-22 08:30+0000\n"
+"Last-Translator: Fajar Ru <kzofajar@gmail.com>\n"
"Language-Team: Indonesian <https://hosted.weblate.org/projects/godot-engine/"
"godot/id/>\n"
"Language: id\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -6483,7 +6482,7 @@ msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr ""
+msgstr "Umum"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
@@ -8350,6 +8349,13 @@ msgid "Invalid font size."
msgstr "Ukuran font tidak sah."
#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Tab sebelumnya"
+
+#~ msgid "Next"
+#~ msgstr "Berikutnya"
+
+#, fuzzy
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "Sambungkan Ke Node:"
@@ -8364,9 +8370,6 @@ msgstr "Ukuran font tidak sah."
#~ msgid "Can't write file."
#~ msgstr "Tidak dapat membuat folder."
-#~ msgid "Next"
-#~ msgstr "Berikutnya"
-
#~ msgid "Not found!"
#~ msgstr "Tidak ditemukan!"
diff --git a/editor/translations/it.po b/editor/translations/it.po
index 9e6833e576..2d566fe163 100644
--- a/editor/translations/it.po
+++ b/editor/translations/it.po
@@ -8449,6 +8449,13 @@ msgstr "Errore caricamento font."
msgid "Invalid font size."
msgstr "Dimensione font Invalida."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Scheda precedente"
+
+#~ msgid "Next"
+#~ msgstr "Successivo"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Azione invalida (va bene tutto a parte '/' o ':')."
@@ -8479,9 +8486,6 @@ msgstr "Dimensione font Invalida."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Impossibile creare project.godot nel percorso di progetto."
-#~ msgid "Next"
-#~ msgstr "Successivo"
-
#~ msgid "Not found!"
#~ msgstr "Non trovato!"
diff --git a/editor/translations/ja.po b/editor/translations/ja.po
index 71b68636f6..5ce73d0442 100644
--- a/editor/translations/ja.po
+++ b/editor/translations/ja.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# akirakido <achts.y@gmail.com>, 2016-2017.
# D_first <dntk.daisei@gmail.com>, 2017, 2018.
# Daisuke Saito <d.saito@coriginate.com>, 2017, 2018.
@@ -14,20 +13,20 @@
# Shinsuke Masuda <shinsuke.masuda@gmail.com>, 2018.
# Tetsuji Ochiai <ochiaixp@gmail.com>, 2017.
# Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>, 2017-2018.
+# yu tang <0011solo@gmail.com>, 2018.
# zukkun <zukkun@gmail.com>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-05-30 15:39+0000\n"
-"Last-Translator: Shinsuke Masuda <shinsuke.masuda@gmail.com>\n"
+"PO-Revision-Date: 2018-06-15 22:40+0000\n"
+"Last-Translator: yu tang <0011solo@gmail.com>\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/"
"godot/ja/>\n"
"Language: ja\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -579,7 +578,6 @@ msgstr "最近ã®:"
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp
-#, fuzzy
msgid "Search:"
msgstr "検索:"
@@ -638,7 +636,6 @@ msgstr "リソース"
#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
#: editor/project_manager.cpp editor/project_settings_editor.cpp
#: editor/script_create_dialog.cpp
-#, fuzzy
msgid "Path"
msgstr "パス"
@@ -1144,7 +1141,7 @@ msgstr "自動読ã¿è¾¼ã¿ã‚’çµ„ã¿æ›¿ãˆã‚‹"
#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
#: scene/gui/file_dialog.cpp
msgid "Path:"
-msgstr "Path:"
+msgstr "パス:"
#: editor/editor_autoload_settings.cpp
#, fuzzy
@@ -1153,7 +1150,6 @@ msgstr "ノードã®åå‰:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
-#, fuzzy
msgid "Name"
msgstr "åå‰"
@@ -1197,7 +1193,7 @@ msgstr "ディレクトリをé¸ã¶"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
msgid "Create Folder"
-msgstr "フォルダを作æˆã™ã‚‹"
+msgstr "フォルダーを作æˆ"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp
@@ -1236,9 +1232,8 @@ msgid "File Exists, Overwrite?"
msgstr "ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚上書ãã—ã¾ã™ã‹ï¼Ÿ"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select Current Folder"
-msgstr "フォルダを作æˆã™ã‚‹"
+msgstr "ç¾åœ¨ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’é¸æŠž"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
@@ -1371,7 +1366,6 @@ msgstr "アセットを(å†ï¼‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Search Help"
msgstr "ヘルプを検索"
@@ -1550,7 +1544,7 @@ msgstr "出力"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "エラーコード %d ã«ã‚ˆã‚Šã€ãƒ—ロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
@@ -1579,9 +1573,8 @@ msgid "Requested file format unknown:"
msgstr "ãã®ãƒ•ã‚¡ã‚¤ãƒ«ã¯æœªçŸ¥ã®ãƒ•ォーマットã§ã™:"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Error while saving."
-msgstr "ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸ."
+msgstr "ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
#: editor/editor_node.cpp
#, fuzzy
@@ -1589,9 +1582,8 @@ msgid "Can't open '%s'."
msgstr "'..'を処ç†ã§ãã¾ã›ã‚“"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Error while parsing '%s'."
-msgstr "ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸ."
+msgstr "「%sã€ã®è§£æžä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
@@ -1603,9 +1595,8 @@ msgid "Missing '%s' or its dependencies."
msgstr "シーン'%s' ã¯ä¾å­˜é–¢ä¿‚ãŒå£Šã‚Œã¦ã„ã¾ã™:"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Error while loading '%s'."
-msgstr "ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸ."
+msgstr "「%sã€ã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
#: editor/editor_node.cpp
msgid "Saving Scene"
@@ -1924,14 +1915,12 @@ msgid "Exit the editor?"
msgstr "エディターを終了ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Open Project Manager?"
-msgstr "プロジェクトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼"
+msgstr "プロジェクトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã‚’é–‹ãã¾ã™ã‹ï¼Ÿ"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Save & Quit"
-msgstr "ファイルをä¿å­˜"
+msgstr "ファイルをä¿å­˜ã—ã¦çµ‚了"
#: editor/editor_node.cpp
msgid "Save changes to the following scene(s) before quitting?"
@@ -2194,7 +2183,7 @@ msgstr "ツール"
#: editor/editor_node.cpp
msgid "Quit to Project List"
-msgstr "終了ã—ã¦ãƒ—ロジェクトリストを開ã"
+msgstr "終了ã—ã¦ãƒ—ロジェクト一覧を開ã"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
#, fuzzy
@@ -2334,7 +2323,6 @@ msgstr "クラス"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
#: editor/plugins/shader_editor_plugin.cpp editor/project_settings_editor.cpp
-#, fuzzy
msgid "Search"
msgstr "検索"
@@ -2480,7 +2468,7 @@ msgstr "ベクトル定数を変更"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Import"
-msgstr "インãƒãƒ¼ãƒˆï¼ˆå–り込ã¿ï¼‰"
+msgstr "インãƒãƒ¼ãƒˆ"
#: editor/editor_node.cpp
#, fuzzy
@@ -3058,7 +3046,7 @@ msgstr "移動"
#: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Rename"
-msgstr "åå‰ã‚’変更ã™ã‚‹"
+msgstr "åå‰ã®å¤‰æ›´"
#: editor/groups_editor.cpp
#, fuzzy
@@ -3705,12 +3693,10 @@ msgid "Connection error, please try again."
msgstr "接続失敗 å†è©¦è¡Œã‚’"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Can't connect to host:"
msgstr "ãƒ›ã‚¹ãƒˆã«æŽ¥ç¶šã§ãã¾ã›ã‚“:"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "No response from host:"
msgstr "ホストã‹ã‚‰å¿œç­”ãŒã‚りã¾ã›ã‚“:"
@@ -6655,9 +6641,8 @@ msgid "Please choose a 'project.godot' file."
msgstr "'project.godot' ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„."
#: editor/project_manager.cpp
-#, fuzzy
msgid "Please choose an empty folder."
-msgstr "'project.godot' ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„."
+msgstr "空ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’é¸æŠžã—ã¦ãã ã•ã„。"
#: editor/project_manager.cpp
msgid "Imported Project"
@@ -6675,7 +6660,7 @@ msgstr "フォルダを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
#: editor/project_manager.cpp
msgid "There is already a folder in this path with the specified name."
-msgstr ""
+msgstr "ã“ã®ãƒ‘スã«ã¯ã€æŒ‡å®šã•れãŸåå‰ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚"
#: editor/project_manager.cpp
msgid "It would be a good idea to name your project."
@@ -6709,9 +6694,8 @@ msgid "The following files failed extraction from package:"
msgstr "以下ã®ãƒ•ァイルをパッケージã‹ã‚‰æŠ½å‡ºã§ãã¾ã›ã‚“ã§ã—ãŸ:"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Rename Project"
-msgstr "åç„¡ã—ã®ãƒ—ロジェクト"
+msgstr "プロジェクトåã®å¤‰æ›´"
#: editor/project_manager.cpp
msgid "New Game Project"
@@ -6728,12 +6712,11 @@ msgstr "インãƒãƒ¼ãƒˆã—ã¦é–‹ã"
#: editor/project_manager.cpp
msgid "Create New Project"
-msgstr "æ–°ã—ã„プロジェクトを作る"
+msgstr "æ–°è¦ãƒ—ロジェクトを作æˆ"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create & Edit"
-msgstr "発光物を生æˆ"
+msgstr "作æˆã—ã¦ç·¨é›†"
#: editor/project_manager.cpp
msgid "Install Project:"
@@ -6745,14 +6728,12 @@ msgid "Install & Edit"
msgstr "インストール"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Project Name:"
msgstr "プロジェクトå:"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create folder"
-msgstr "フォルダを作æˆã™ã‚‹"
+msgstr "フォルダを作æˆ"
#: editor/project_manager.cpp
msgid "Project Path:"
@@ -6760,16 +6741,15 @@ msgstr "プロジェクトパス:"
#: editor/project_manager.cpp
msgid "Browse"
-msgstr "ブラウズ"
+msgstr "å‚照…"
#: editor/project_manager.cpp
msgid "Unnamed Project"
msgstr "åç„¡ã—ã®ãƒ—ロジェクト"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Can't open project"
-msgstr "接続失敗."
+msgstr "プロジェクトを開ã‘ã¾ã›ã‚“"
#: editor/project_manager.cpp
msgid "Are you sure to open more than one project?"
@@ -6797,19 +6777,17 @@ msgid "Are you sure to run more than one project?"
msgstr "複数ã®ãƒ—ロジェクトを本当ã«å®Ÿè¡Œã—ã¾ã™ã‹ï¼Ÿ"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Remove project from the list? (Folder contents will not be modified)"
msgstr ""
-"リストã‹ã‚‰ãƒ—ロジェクトを除去ã—ã¾ã™ã‹ï¼Ÿï¼ˆãƒ•ォルダーã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯å½±éŸ¿ã‚’å—ã‘ã¾"
-"ã›ã‚“)"
+"一覧ã‹ã‚‰ãƒ—ロジェクトを削除ã—ã¾ã™ã‹ï¼Ÿï¼ˆãƒ•ォルダーã®å†…容ã¯å¤‰æ›´ã•れã¾ã›ã‚“)"
#: editor/project_manager.cpp
msgid ""
"Language changed.\n"
"The UI will update next time the editor or project manager starts."
msgstr ""
-"言語ãŒå¤‰æ›´ã•れã¾ã—ãŸ.\n"
-"エディタã¾ãŸã¯ãƒ—ロジェクトマãƒã‚¸ãƒ£ãƒ¼å†é–‹æ™‚ã«UIãŒæ›´æ–°ã•れã¾ã™."
+"言語ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚\n"
+"エディターã¾ãŸã¯ãƒ—ロジェクトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼å†èµ·å‹•後ã«UIãŒæ›´æ–°ã•れã¾ã™ã€‚"
#: editor/project_manager.cpp
msgid ""
@@ -6823,7 +6801,7 @@ msgstr "プロジェクトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼"
#: editor/project_manager.cpp
msgid "Project List"
-msgstr "プロジェクトã®ãƒªã‚¹ãƒˆ"
+msgstr "プロジェクト一覧"
#: editor/project_manager.cpp
msgid "Scan"
@@ -6835,34 +6813,31 @@ msgstr "スキャンã™ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’é¸æŠž"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr "æ–°ã—ã„プロジェクト"
+msgstr "æ–°è¦ãƒ—ロジェクト"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Templates"
-msgstr "é¸æŠžã—ã¦ã„ã‚‹ã‚‚ã®ã‚’削除"
+msgstr "テンプレート"
#: editor/project_manager.cpp
msgid "Exit"
msgstr "終了"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Restart Now"
-msgstr "アニメーションを最åˆã‹ã‚‰å†ç”Ÿã™ã‚‹ :"
+msgstr "今ã™ãå†èµ·å‹•"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Can't run project"
-msgstr "接続失敗."
+msgstr "プロジェクトを実行ã§ãã¾ã›ã‚“"
#: editor/project_manager.cpp
msgid ""
"You don't currently have any projects.\n"
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
-"ã‚ãªãŸã¯ç¾åœ¨ã©ã®ãƒ—ロジェクトもæŒã£ã¦ã„ã¾ã›ã‚“。\n"
-"アセットライブラリã§å…¬å¼ã®ã‚µãƒ³ãƒ—ルプロジェクトを探ã—ã¾ã—ょã†ã‹ï¼Ÿ"
+"プロジェクトãŒä½•も登録ã•れã¦ã„ã¾ã›ã‚“。\n"
+"アセットライブラリã§å…¬å¼ã®ã‚µãƒ³ãƒ—ルプロジェクトをãƒã‚§ãƒƒã‚¯ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/project_settings_editor.cpp
#, fuzzy
@@ -9050,7 +9025,7 @@ msgstr "警告!"
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr "確èªã—ã¦ãã ã•ã„。"
+msgstr "確èª"
#: scene/gui/file_dialog.cpp
#, fuzzy
@@ -9119,6 +9094,13 @@ msgstr "フォント読ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã€‚"
msgid "Invalid font size."
msgstr "無効ãªãƒ•ォント サイズã§ã™ã€‚"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "以å‰ã®ã‚¿ãƒ–"
+
+#~ msgid "Next"
+#~ msgstr "次"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "䏿­£ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ï¼ˆ '/' ã¨':'ã¯ä¸å¯ã§ã™ï¼‰."
@@ -9147,9 +9129,6 @@ msgstr "無効ãªãƒ•ォント サイズã§ã™ã€‚"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "project.godotをプロジェクトパスã«ç”Ÿæˆã§ãã¾ã›ã‚“ã§ã—ãŸ"
-#~ msgid "Next"
-#~ msgstr "次"
-
#~ msgid "Not found!"
#~ msgstr "見ã¤ã‹ã‚Šã¾ã›ã‚“!"
diff --git a/editor/translations/ko.po b/editor/translations/ko.po
index 575c14c97c..be6b540a9a 100644
--- a/editor/translations/ko.po
+++ b/editor/translations/ko.po
@@ -2,21 +2,20 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Ch <ccwpc@hanmail.net>, 2017.
# paijai 송 (fivejobi) <xotjq237@gmail.com>, 2018.
+# pgyage3263 <pgyage3263@naver.com>, 2018.
# Sun Kim <perplexingsun@gmail.com>, 2018.
# TheRedPlanet <junmo.moon8@gmail.com>, 2018.
# Xavier Cho <mysticfallband@gmail.com>, 2018.
# 박한얼 (volzhs) <volzhs@gmail.com>, 2016-2018.
# 송태섭 <xotjq237@gmail.com>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-05-27 04:39+0000\n"
-"Last-Translator: 송태섭 <xotjq237@gmail.com>\n"
+"PO-Revision-Date: 2018-06-07 16:40+0000\n"
+"Last-Translator: pgyage3263 <pgyage3263@naver.com>\n"
"Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/"
"godot/ko/>\n"
"Language: ko\n"
@@ -24,7 +23,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5677,9 +5676,8 @@ msgid "Options"
msgstr "옵션"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "가진다,ë§Žì€,여러,옵션들!"
+msgstr "가진다,ë§Žì€,옵션들"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5960,9 +5958,8 @@ msgid "Imported Project"
msgstr "가져온 프로ì íЏ"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "프로ì íЏ 명:"
+msgstr "ì¸ì‹í• ìˆ˜ 없는 프로ì íЏ 명입니다."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6162,13 +6159,12 @@ msgid "Mouse Button"
msgstr "마우스 버튼"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
-"올바르지 ì•Šì€ ì•¡ì…˜ ì´ë¦„. 공백ì´ê±°ë‚˜ '/' ì´ë‚˜ ':', '=', '\\', '\"' ê°€ í¬í•¨ë˜"
-"ë©´ 안 ë©ë‹ˆë‹¤"
+"ì¸ì‹í• ìˆ˜ 없는 ì•¡ì…˜ ì´ë¦„입니다. 공백ì´ê±°ë‚˜, '/' , ':', '=', '\\', '\"' ê°€ í¬í•¨"
+"ë˜ë©´ 안 ë©ë‹ˆë‹¤."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8177,6 +8173,13 @@ msgstr "í°íЏ 로딩 ì—러."
msgid "Invalid font size."
msgstr "유효하지 ì•Šì€ í°íЏ í¬ê¸°."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ì´ì „ 탭"
+
+#~ msgid "Next"
+#~ msgstr "다ìŒ"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "유효하지 ì•Šì€ ì•¡ì…˜ ('/' ë˜ëŠ” ':' ë¬¸ìž ì‚¬ìš© 불가)."
@@ -8202,9 +8205,6 @@ msgstr "유효하지 ì•Šì€ í°íЏ í¬ê¸°."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "프로ì íЏ ê²½ë¡œì— project.godot 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다."
-#~ msgid "Next"
-#~ msgstr "다ìŒ"
-
#~ msgid "Not found!"
#~ msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!"
diff --git a/editor/translations/lt.po b/editor/translations/lt.po
index aaf6fc4499..bf4443627a 100644
--- a/editor/translations/lt.po
+++ b/editor/translations/lt.po
@@ -2,14 +2,12 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Ignas Kiela <ignaskiela@super.lt>, 2017.
-# Kornelijus <kornelijus.github@gmail.com>, 2017.
-#
+# Kornelijus <kornelijus.github@gmail.com>, 2017, 2018.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-11-27 00:48+0000\n"
+"PO-Revision-Date: 2018-06-12 09:40+0000\n"
"Last-Translator: Kornelijus <kornelijus.github@gmail.com>\n"
"Language-Team: Lithuanian <https://hosted.weblate.org/projects/godot-engine/"
"godot/lt/>\n"
@@ -18,7 +16,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n%10>=2 && (n%100<10 || n"
"%100>=20) ? 1 : n%10==0 || (n%100>10 && n%100<20) ? 2 : 3;\n"
-"X-Generator: Weblate 2.18-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -2022,11 +2020,11 @@ msgstr ""
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
-msgstr ""
+msgstr "BendruomenÄ—"
#: editor/editor_node.cpp
msgid "About"
-msgstr ""
+msgstr "Apie"
#: editor/editor_node.cpp
msgid "Play the project."
diff --git a/editor/translations/nb.po b/editor/translations/nb.po
index 1637ef9725..e76053150c 100644
--- a/editor/translations/nb.po
+++ b/editor/translations/nb.po
@@ -2,27 +2,27 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Allan Nordhøy <epost@anotheragency.no>, 2017-2018.
# Anonymous <GentleSaucepan@protonmail.com>, 2017.
+# Elias <eliasnykrem@gmail.com>, 2018.
# flesk <eivindkn@gmail.com>, 2017.
+# Frank T. Rambol <frank@d-fect.com>, 2018.
# Jørgen Aarmo Lund <jorgen.aarmo@gmail.com>, 2016.
# NicolaiF <nico-fre@hotmail.com>, 2017-2018.
# Norwegian Disaster <stian.furu.overbye@gmail.com>, 2017.
# passeride <lukas@passeride.com>, 2017.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-03-22 03:38+0000\n"
-"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
+"PO-Revision-Date: 2018-06-22 08:31+0000\n"
+"Last-Translator: Frank T. Rambol <frank@d-fect.com>\n"
"Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/godot-"
"engine/godot/nb/>\n"
"Language: nb\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -33,9 +33,8 @@ msgid "All Selection"
msgstr "Alle valg"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "Anim Forandre Verdi"
+msgstr "Anim Endre Nøkkelbildetid"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
@@ -46,9 +45,8 @@ msgid "Anim Change Transform"
msgstr "Anim Forandre Omforming"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Anim Forandre Verdi"
+msgstr "Anim Endre Nøkkelbildeverdi"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -99,9 +97,8 @@ msgid "Edit Node Curve"
msgstr "Forandre Nodekurve"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Edit Selection Curve"
-msgstr "Forandre utvalgskurve"
+msgstr "Rediger utvalgskurve"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
@@ -501,9 +498,8 @@ msgid "Connecting Signal:"
msgstr "Kobler Til Signal:"
#: editor/connections_dialog.cpp
-#, fuzzy
msgid "Disconnect '%s' from '%s'"
-msgstr "Koble '%s' til '%s'"
+msgstr "Koble '%s' fra '%s'"
#: editor/connections_dialog.cpp
msgid "Connect..."
@@ -519,9 +515,8 @@ msgid "Signals"
msgstr "Signaler"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Change %s Type"
-msgstr "Endre standard type"
+msgstr "Endre %s type"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -529,9 +524,8 @@ msgid "Change"
msgstr "Forandre"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Create New %s"
-msgstr "Lag Ny"
+msgstr "Lag ny %s"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -598,7 +592,7 @@ msgstr "Ressurs"
#: editor/project_manager.cpp editor/project_settings_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Path"
-msgstr "Sti"
+msgstr "Søkesti"
#: editor/dependency_editor.cpp
msgid "Dependencies:"
@@ -642,9 +636,8 @@ msgstr ""
"Fjern dem likevel? (kan ikke angres)"
#: editor/dependency_editor.cpp
-#, fuzzy
msgid "Cannot remove:"
-msgstr "Kan ikke fjerne:\n"
+msgstr "Kan ikke fjerne:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
@@ -729,9 +722,8 @@ msgid "Lead Developer"
msgstr "Utviklingsleder"
#: editor/editor_about.cpp
-#, fuzzy
msgid "Project Manager "
-msgstr "Prosjektleder"
+msgstr "Prosjektleder "
#: editor/editor_about.cpp
msgid "Developers"
@@ -840,9 +832,8 @@ msgid "Rename Audio Bus"
msgstr "Gi nytt navn til Audio Bus"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Change Audio Bus Volume"
-msgstr "Veksle Audio Bus Solo"
+msgstr "Endre Lydbuss Volum"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
@@ -885,7 +876,6 @@ msgid "Mute"
msgstr "Demp"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Bypass"
msgstr "Omgå"
@@ -1131,9 +1121,8 @@ msgid "Packing"
msgstr "Pakking"
#: editor/editor_export.cpp platform/javascript/export/export.cpp
-#, fuzzy
msgid "Template file not found:"
-msgstr "Malfil ble ikke funnet:\n"
+msgstr "Malfil ble ikke funnet:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "File Exists, Overwrite?"
@@ -1344,19 +1333,18 @@ msgid "Description"
msgstr "Beskrivelse"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Online Tutorials:"
-msgstr "Online Dokumentasjon"
+msgstr "Online dokumentasjon:"
#: editor/editor_help.cpp
-#, fuzzy
msgid ""
"There are currently no tutorials for this class, you can [color=$color][url="
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
-"Det finnes i øyeblikket ingen beskrivelse av denne metoden. Hjelp til ved å "
-"[colour=$color][url=$url]bidra med en[/url][/color]!"
+"Det finnes i øyeblikket ingen beskrivelse av denne metoden, men du kan "
+"[colour=$color][url=$url]bidra med en[/url][/color] eller [color=$color][url="
+"$url2]be om en[/url][/color]."
#: editor/editor_help.cpp
msgid "Properties"
@@ -1410,13 +1398,12 @@ msgid "Clear"
msgstr "Tøm"
#: editor/editor_log.cpp
-#, fuzzy
msgid "Clear Output"
-msgstr "Output"
+msgstr "Nullstill resultat"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr ""
+msgstr "Eksport av prosjektet mislyktes med feilkode %d."
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
@@ -1428,9 +1415,8 @@ msgstr "Lagre Ressurs Som..."
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "I see..."
-msgstr "Jeg ser..."
+msgstr "Jeg forstår..."
#: editor/editor_node.cpp
msgid "Can't open file for writing:"
@@ -1453,9 +1439,8 @@ msgid "Error while parsing '%s'."
msgstr "Error ved parsing av '%s'."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Unexpected end of file '%s'."
-msgstr "Uventet ende av fil '%s'."
+msgstr "Uventet slutt på fil '%s'."
#: editor/editor_node.cpp
msgid "Missing '%s' or its dependencies."
@@ -1482,13 +1467,12 @@ msgid "This operation can't be done without a tree root."
msgstr "Denne operasjonen kan ikke gjennomføres uten en trerot."
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
msgstr ""
-"Kunne ikke lagre scene. Sannsynligvis avhengigheter (instanser) ikke kunne "
-"oppfylles."
+"Kunne ikke lagre scene. Sannsynligvis kunne ikke avhengigheter (instanser "
+"eller arvinger) oppfylles."
#: editor/editor_node.cpp
msgid "Failed to load resource."
@@ -1600,14 +1584,12 @@ msgid "Copy Resource"
msgstr "Kopier Ressurs"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Make Built-In"
-msgstr "Lag Innebygd"
+msgstr "Lag innebygget"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Make Sub-Resources Unique"
-msgstr "Gjør Underressurs Unik"
+msgstr "Lag underressurser unike"
#: editor/editor_node.cpp
msgid "Open in Help"
@@ -1628,14 +1610,13 @@ msgstr ""
"'applikasjon'."
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"Selected scene '%s' does not exist, select a valid one?\n"
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"Valgte scene '%s' finnes ikke, velg en gyldig en?\n"
-"Du kan endre dette senere under \"Prosjekt Innstillinger\" under kategorien "
+"Den valgte scenen '%s' finnes ikke. Vil du velge en gyldig scene?\n"
+"Du kan endre dette senere i \"Prosjektinnstillinger\" under kategorien "
"'applikasjon'."
#: editor/editor_node.cpp
@@ -1933,7 +1914,7 @@ msgstr "MeshBibliotek..."
#: editor/editor_node.cpp
#, fuzzy
msgid "TileSet..."
-msgstr "TileSet..."
+msgstr "TileSet…"
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
@@ -1983,9 +1964,8 @@ msgid "Debug"
msgstr "Debug"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Deploy with Remote Debug"
-msgstr "Deploy med Ekstern Debug"
+msgstr "Distribuer med ekstern feilsøking"
#: editor/editor_node.cpp
#, fuzzy
@@ -2145,7 +2125,7 @@ msgstr "Pause scenen"
#: editor/editor_node.cpp
msgid "Pause Scene"
-msgstr "Pause Scene"
+msgstr "Sett scenen på pause"
#: editor/editor_node.cpp
msgid "Stop the scene."
@@ -8214,12 +8194,19 @@ msgstr ""
#: scene/resources/dynamic_font.cpp
msgid "Error loading font."
-msgstr ""
+msgstr "Feil ved innlasting av font."
#: scene/resources/dynamic_font.cpp
msgid "Invalid font size."
msgstr "Ugyldig fontstørrelse."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Forrige fane"
+
+#~ msgid "Next"
+#~ msgstr "Neste"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
@@ -8230,9 +8217,6 @@ msgstr "Ugyldig fontstørrelse."
#~ msgid "Can't write file."
#~ msgstr "Kan ikke skrive fil."
-#~ msgid "Next"
-#~ msgstr "Neste"
-
#~ msgid "Not found!"
#~ msgstr "Ikke funnet!"
diff --git a/editor/translations/nl.po b/editor/translations/nl.po
index 3bf8ab3a32..bfedf322b3 100644
--- a/editor/translations/nl.po
+++ b/editor/translations/nl.po
@@ -8272,6 +8272,13 @@ msgid "Invalid font size."
msgstr "Ongeldige lettertype grootte."
#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Vorig tabblad"
+
+#~ msgid "Next"
+#~ msgstr "Volgende"
+
+#, fuzzy
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "Kan niet verbinden met host:"
@@ -8285,9 +8292,6 @@ msgstr "Ongeldige lettertype grootte."
#~ msgid "Can't write file."
#~ msgstr "Kan niet naar bestand schrijven."
-#~ msgid "Next"
-#~ msgstr "Volgende"
-
#~ msgid "Not found!"
#~ msgstr "Niet gevonden!"
diff --git a/editor/translations/pl.po b/editor/translations/pl.po
index 1a597cee5a..5ca2760249 100644
--- a/editor/translations/pl.po
+++ b/editor/translations/pl.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# 8-bit Pixel <dawdejw@gmail.com>, 2016.
# Adam Wolanski <adam.wolanski94@gmail.com>, 2017.
# Adrian Węcławski <weclawskiadrian@gmail.com>, 2016.
@@ -25,11 +24,10 @@
# Sebastian Pasich <sebastian.pasich@gmail.com>, 2017.
# siatek papieros <sbigneu@gmail.com>, 2016.
# Zatherz <zatherz@linux.pl>, 2017.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-06-05 13:41+0000\n"
+"PO-Revision-Date: 2018-06-22 08:31+0000\n"
"Last-Translator: RM <synaptykq@gmail.com>\n"
"Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/"
"godot/pl/>\n"
@@ -38,7 +36,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 3.0\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5354,7 +5352,7 @@ msgstr "Tryb skalowania (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
-msgstr "Koordynaty lokalne"
+msgstr "Local Coords"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
@@ -8294,6 +8292,13 @@ msgstr "Błąd ładowania fonta."
msgid "Invalid font size."
msgstr "Niepoprawny rozmiar fonta."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Poprzednia zakładka"
+
+#~ msgid "Next"
+#~ msgstr "Następny"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Nieprawidłowa akcja (wszystko oprócz '/' lub ':')."
@@ -8319,9 +8324,6 @@ msgstr "Niepoprawny rozmiar fonta."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Nie znaleziono project.godot w ścieżce projektu."
-#~ msgid "Next"
-#~ msgstr "Następny"
-
#~ msgid "Not found!"
#~ msgstr "Nie znaleziono!"
diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po
index 9880271a0d..6d26cbc500 100644
--- a/editor/translations/pt_BR.po
+++ b/editor/translations/pt_BR.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Allyson Souza <allyson_as@outlook.com>, 2017.
# Anderson Araujo <anderson.araujoprog@gmail.com>, 2018.
# António Sarmento <antonio.luis.sarmento@gmail.com>, 2016.
@@ -24,14 +23,12 @@
# Renato Rotenberg <renato.rotenberg@gmail.com>, 2017.
# Rodolfo R Gomes <rodolforg@gmail.com>, 2017-2018.
# Tiago Almeida <thyagoeap@gmail.com>, 2017.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: 2016-05-30\n"
-"PO-Revision-Date: 2018-05-20 21:41+0000\n"
-"Last-Translator: Michael Alexsander Silva Dias <michaelalexsander@protonmail."
-"com>\n"
+"PO-Revision-Date: 2018-06-16 18:43+0000\n"
+"Last-Translator: Rodolfo R Gomes <rodolforg@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"godot-engine/godot/pt_BR/>\n"
"Language: pt_BR\n"
@@ -39,7 +36,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -525,7 +522,7 @@ msgstr "Conectar..."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Disconnect"
-msgstr "Disconectar"
+msgstr "Desconectar"
#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
msgid "Signals"
@@ -3308,7 +3305,7 @@ msgstr "AnimationTree"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
-msgstr "Livrar"
+msgstr "Livre"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Contents:"
@@ -5717,9 +5714,8 @@ msgid "Options"
msgstr "Opções"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Tem,Muitas,Várias,Opções!"
+msgstr "Tem,Muitas,Opções"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -6008,9 +6004,8 @@ msgid "Imported Project"
msgstr "Projeto Importado"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nome do Projeto:"
+msgstr "Nome do Projeto Inválido."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6212,13 +6207,12 @@ msgid "Mouse Button"
msgstr "Botão do Mous"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Nome de ação inválido. Ele não pode estar vazio ou conter '/', ':', '=', "
-"'\\' ou '\"'"
+"'\\' ou '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8246,6 +8240,13 @@ msgstr "Erro ao carregar fonte."
msgid "Invalid font size."
msgstr "Tamanho de fonte inválido."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Guia anterior"
+
+#~ msgid "Next"
+#~ msgstr "Próximo"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Ação Inválida (qualquer coisa serve, exceto '/' ou ':')."
@@ -8272,9 +8273,6 @@ msgstr "Tamanho de fonte inválido."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Não foi possível encontrar project.godot no caminho do projeto."
-#~ msgid "Next"
-#~ msgstr "Próximo"
-
#~ msgid "Not found!"
#~ msgstr "Não encontrado!"
diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po
index 0d6a1c27a5..71275cd19a 100644
--- a/editor/translations/pt_PT.po
+++ b/editor/translations/pt_PT.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# António Sarmento <antonio.luis.sarmento@gmail.com>, 2016.
# Carlos Vieira <carlos.vieira@gmail.com>, 2017.
# João <joao@nogordio.com>, 2018.
@@ -14,19 +13,18 @@
# Rueben Stevens <supercell03@gmail.com>, 2017.
# SARDON <fabio3_Santos@hotmail.com>, 2017.
# Vinicius Gonçalves <viniciusgoncalves21@gmail.com>, 2017.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-05-26 14:42+0000\n"
-"Last-Translator: João <joao@nogordio.com>\n"
+"PO-Revision-Date: 2018-06-10 01:02+0000\n"
+"Last-Translator: João Lopes <linux-man@hotmail.com>\n"
"Language-Team: Portuguese (Portugal) <https://hosted.weblate.org/projects/"
"godot-engine/godot/pt_PT/>\n"
"Language: pt_PT\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5697,9 +5695,8 @@ msgid "Options"
msgstr "Opções"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Tem,Muitos,Vários,Opções!"
+msgstr "Tem,Muitas,Opções"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5986,9 +5983,8 @@ msgid "Imported Project"
msgstr "Projeto importado"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nome do Projeto:"
+msgstr "Nome do Projeto Inválido."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6189,13 +6185,12 @@ msgid "Mouse Button"
msgstr "Botão do rato"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Nome de ação inválido. Não pode ser vazio nem conter '/', ':', '=', '\\' ou "
-"'\"'"
+"'\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8224,6 +8219,13 @@ msgstr "Erro ao carregar letra."
msgid "Invalid font size."
msgstr "Tamanho de letra inválido."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Guia anterior"
+
+#~ msgid "Next"
+#~ msgstr "Proximo"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Ação inválida (tudo menos '/' ou ':')."
@@ -8249,9 +8251,6 @@ msgstr "Tamanho de letra inválido."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Impossível encontrar project.godot no Caminho do Projeto."
-#~ msgid "Next"
-#~ msgstr "Proximo"
-
#~ msgid "Not found!"
#~ msgstr "Não encontrado!"
diff --git a/editor/translations/ro.po b/editor/translations/ro.po
index 9358c958a0..eaf931092a 100644
--- a/editor/translations/ro.po
+++ b/editor/translations/ro.po
@@ -2,16 +2,15 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
+# Calin Sopterean <csopterean@gmail.com>, 2018.
# Filip <filipanton@tutanota.com>, 2018.
# Nitroretro <nitroretro@protonmail.com>, 2018.
# TigerxWood <TigerxWood@gmail.com>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-05-10 20:02+0000\n"
-"Last-Translator: Nitroretro <nitroretro@protonmail.com>\n"
+"PO-Revision-Date: 2018-06-20 20:43+0000\n"
+"Last-Translator: Calin Sopterean <csopterean@gmail.com>\n"
"Language-Team: Romanian <https://hosted.weblate.org/projects/godot-engine/"
"godot/ro/>\n"
"Language: ro\n"
@@ -19,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -257,7 +256,7 @@ msgstr "Pas (s):"
#: editor/animation_editor.cpp
msgid "Cursor step snap (in seconds)."
-msgstr "Pas Bruscare Cursor (în secunde)."
+msgstr "Pas de Cursor Snap (în secunde)."
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
@@ -550,7 +549,7 @@ msgstr "Potriviri:"
#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp
#: editor/script_editor_debugger.cpp
msgid "Description:"
-msgstr "Descripție:"
+msgstr "Descriere:"
#: editor/dependency_editor.cpp
msgid "Search Replacement For:"
@@ -794,7 +793,7 @@ msgstr "Eroare la deschiderea fişierului pachet, nu este în format zip."
#: editor/editor_asset_installer.cpp
msgid "Uncompressing Assets"
-msgstr "Decompresez Active"
+msgstr "Decomprimare Asset-uri"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Package Installed Successfully!"
@@ -893,7 +892,7 @@ msgstr "Ștergeți Efectul"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr "Audio"
+msgstr "Sunet"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -921,15 +920,15 @@ msgstr "Mutați Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Save Audio Bus Layout As..."
-msgstr "Salvați Pista Audio Șablon Ca..."
+msgstr "Salvați Schema Pistei Audio Ca..."
#: editor/editor_audio_buses.cpp
msgid "Location for New Layout..."
-msgstr "Locație pentru Noul Șablon..."
+msgstr "Locație pentru Noua Schemă..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr "Deschideți Șablon Pistă Audio"
+msgstr "Deschide Schema Pistei Audio"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
@@ -937,7 +936,7 @@ msgstr "Nu există nici un fişier 'res://default_bus_layout.tres'."
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
-msgstr "Fişier nevalid, nu un șablon de pistă audio."
+msgstr "Fişier nevalid, nu este o schemă de pistă audio."
#: editor/editor_audio_buses.cpp
msgid "Add Bus"
@@ -945,7 +944,7 @@ msgstr "Adaugați Pistă Audio"
#: editor/editor_audio_buses.cpp
msgid "Create a new Bus Layout."
-msgstr "Creaţi un Șablon nou Pistă Audio."
+msgstr "Creaţi o Schemă nouă de Pistă Audio."
#: editor/editor_audio_buses.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
@@ -954,7 +953,7 @@ msgstr "Încărcați"
#: editor/editor_audio_buses.cpp
msgid "Load an existing Bus Layout."
-msgstr "Încărcaţi un Șablon de Pistă Audio existent."
+msgstr "Încărcaţi o Schemă de Pistă Audio existentă."
#: editor/editor_audio_buses.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
@@ -963,7 +962,7 @@ msgstr "Salvați Ca"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr "Salvaţi acest Șablon Pistă Audio într-un fişier."
+msgstr "Salvaţi acestă Schemă de Pistă Audio într-un fişier."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
@@ -971,7 +970,7 @@ msgstr "Încărcați Implicit"
#: editor/editor_audio_buses.cpp
msgid "Load the default Bus Layout."
-msgstr "Încărcat Șablonul Pistă Audio implicit."
+msgstr "Încarcă Schema de Pistă Audio implicită."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name."
@@ -1242,7 +1241,7 @@ msgstr "SurseScan"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
-msgstr "(Re)Importând Bunuri"
+msgstr "(Re)Importând Asset-uri"
#: editor/editor_help.cpp editor/editor_node.cpp
#: editor/plugins/script_editor_plugin.cpp
@@ -1393,11 +1392,11 @@ msgstr "Afișare:"
#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
msgid "Clear"
-msgstr "Goliți"
+msgstr "Curăță"
#: editor/editor_log.cpp
msgid "Clear Output"
-msgstr "Goliți Afișarea"
+msgstr "Curăță Afișarea"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
@@ -1498,15 +1497,15 @@ msgstr "Eroare la încercarea de a salva schema!"
#: editor/editor_node.cpp
msgid "Default editor layout overridden."
-msgstr "Șablon Editor implicit suprascris."
+msgstr "Schemă implicită de editor suprascrisă."
#: editor/editor_node.cpp
msgid "Layout name not found!"
-msgstr "Numele șablonului nu a fost găsit!"
+msgstr "Numele schemei nu a fost găsit!"
#: editor/editor_node.cpp
msgid "Restored default layout to base settings."
-msgstr "Restaurat șablonul implicit la setările de bază."
+msgstr "S-a restaurat schema implictă la setările de bază."
#: editor/editor_node.cpp
msgid ""
@@ -1597,7 +1596,7 @@ msgstr "Deschideți în Ajutor"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr "Nu există nici o scenă definită pentru a rula."
+msgstr "Nu există nici o scenă definită pentru a execuție."
#: editor/editor_node.cpp
msgid ""
@@ -1681,288 +1680,306 @@ msgstr "Această operație nu se poate face fără o scenă."
#: editor/editor_node.cpp
msgid "Export Mesh Library"
-msgstr ""
+msgstr "Exportă Librăria de Mesh-uri"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr ""
+msgstr "Această operațiune nu poate fi făcută fără un nod de bază."
#: editor/editor_node.cpp
msgid "Export Tile Set"
-msgstr ""
+msgstr "Exportă Setul de Plăci"
#: editor/editor_node.cpp
msgid "This operation can't be done without a selected node."
-msgstr ""
+msgstr "Această operațiune nu poate fi făcută fără un nod selectat."
#: editor/editor_node.cpp
msgid "Current scene not saved. Open anyway?"
-msgstr ""
+msgstr "Scena curentă nu este salvată. Deschizi oricum?"
#: editor/editor_node.cpp
msgid "Can't reload a scene that was never saved."
-msgstr ""
+msgstr "Nu se poate reîncărca o scenă care nu a fost salvată niciodată."
#: editor/editor_node.cpp
msgid "Revert"
-msgstr ""
+msgstr "ÃŽntoarcere"
#: editor/editor_node.cpp
msgid "This action cannot be undone. Revert anyway?"
-msgstr ""
+msgstr "Această acțiune nu poate fi recuperată. Te reîntorci oricum?"
#: editor/editor_node.cpp
msgid "Quick Run Scene..."
-msgstr ""
+msgstr "Execută Rapid Scena..."
#: editor/editor_node.cpp
msgid "Quit"
-msgstr ""
+msgstr "ÃŽnchidere"
#: editor/editor_node.cpp
msgid "Exit the editor?"
-msgstr ""
+msgstr "Ieși din editor?"
#: editor/editor_node.cpp
msgid "Open Project Manager?"
-msgstr ""
+msgstr "Deschizi Managerul de Proiect?"
#: editor/editor_node.cpp
msgid "Save & Quit"
-msgstr ""
+msgstr "Salvează și Închide"
#: editor/editor_node.cpp
msgid "Save changes to the following scene(s) before quitting?"
msgstr ""
+"Salvezi modificările făcute în urmatoarea(le) scenă(e) înainte să închizi?"
#: editor/editor_node.cpp
msgid "Save changes the following scene(s) before opening Project Manager?"
msgstr ""
+"Salvezi modificările făcute în urmatoarea(le) scenă(e) înainte să deschizi "
+"Managerul de Proiect?"
#: editor/editor_node.cpp
msgid ""
"This option is deprecated. Situations where refresh must be forced are now "
"considered a bug. Please report."
msgstr ""
+"Această opțiune este depreciată. Situațiile în care reînprospătarea trebuie "
+"forțată sunt acum considerate buguri. Te rugăm să raportezi."
#: editor/editor_node.cpp
msgid "Pick a Main Scene"
-msgstr ""
+msgstr "Alege o Scenă Principală"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
msgstr ""
+"Nu se poate inițializa plugin-ul la: '%s' analizarea configurației a eșuat."
#: editor/editor_node.cpp
msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
msgstr ""
+"Nu a putut fi găsit câmpul scriptului pentru plugin la: 'res://addons/%s'."
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s'."
-msgstr ""
+msgstr "Nu a putut fi încărcat scriptul add-on din calea: '%s'."
#: editor/editor_node.cpp
msgid ""
"Unable to load addon script from path: '%s' Base type is not EditorPlugin."
msgstr ""
+"Nu a putut fi încărcat scriptul add-on din calea: '%s' tipul de Bază nu este "
+"EditorPlugin."
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s' Script is not in tool mode."
msgstr ""
+"Nu a putut fi încărcat scriptul add-on din calea: '%s' Scriptul nu este în "
+"modul unealtă."
#: editor/editor_node.cpp
msgid ""
"Scene '%s' was automatically imported, so it can't be modified.\n"
"To make changes to it, a new inherited scene can be created."
msgstr ""
+"Scena '%s' nu a fost importată automat, deci ea nu poate fi modificată.\n"
+"Ca să poți face modificări, o nouă scenă derivată poate fi creată."
#: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ugh"
-msgstr ""
+msgstr "Uh"
#: editor/editor_node.cpp
msgid ""
"Error loading scene, it must be inside the project path. Use 'Import' to "
"open the scene, then save it inside the project path."
msgstr ""
+"Eroare la încărcarea scenei, aceasta trebuie să fie în calea spre proiect. "
+"Folosește 'Importă' ca să deschizi scena, apoi salveaz-o în calea spre "
+"proiect."
#: editor/editor_node.cpp
msgid "Scene '%s' has broken dependencies:"
-msgstr ""
+msgstr "Scena '%s' are dependințe nefuncționale:"
#: editor/editor_node.cpp
msgid "Clear Recent Scenes"
-msgstr ""
+msgstr "Curăță Scenele Recente"
#: editor/editor_node.cpp
msgid "Save Layout"
-msgstr ""
+msgstr "Salvează Schema"
#: editor/editor_node.cpp
msgid "Delete Layout"
-msgstr ""
+msgstr "Șterge Schema"
#: editor/editor_node.cpp editor/import_dock.cpp
#: editor/script_create_dialog.cpp
msgid "Default"
-msgstr ""
+msgstr "Implicit"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr ""
+msgstr "Comutați între Scene"
#: editor/editor_node.cpp
msgid "%d more files or folders"
-msgstr ""
+msgstr "%d mai multe fișiere sau foldere"
#: editor/editor_node.cpp
msgid "%d more folders"
-msgstr ""
+msgstr "%d mai multe foldere"
#: editor/editor_node.cpp
msgid "%d more files"
-msgstr ""
+msgstr "%d mai multe fișiere"
#: editor/editor_node.cpp
msgid "Dock Position"
-msgstr ""
+msgstr "Poziția Dock-ului"
#: editor/editor_node.cpp
msgid "Distraction Free Mode"
-msgstr ""
+msgstr "Modul Fără Distrageri"
#: editor/editor_node.cpp
msgid "Toggle distraction-free mode."
-msgstr ""
+msgstr "Comutează modul fără distrageri."
#: editor/editor_node.cpp
msgid "Add a new scene."
-msgstr ""
+msgstr "Adaugă o nouă scenă."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr ""
+msgstr "Scenă"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
-msgstr ""
+msgstr "Mergi la o scenă deschisă anterior."
#: editor/editor_node.cpp
msgid "Next tab"
-msgstr ""
+msgstr "Fila următoare"
#: editor/editor_node.cpp
msgid "Previous tab"
-msgstr ""
+msgstr "Fila anterioară"
#: editor/editor_node.cpp
msgid "Filter Files..."
-msgstr ""
+msgstr "Filtrează fișierele..."
#: editor/editor_node.cpp
msgid "Operations with scene files."
-msgstr ""
+msgstr "Operațiuni cu fișiere tip scenă."
#: editor/editor_node.cpp
msgid "New Scene"
-msgstr ""
+msgstr "Scenă Nouă"
#: editor/editor_node.cpp
msgid "New Inherited Scene..."
-msgstr ""
+msgstr "Scenă Derivată Nouă..."
#: editor/editor_node.cpp
msgid "Open Scene..."
-msgstr ""
+msgstr "Deschide Scena..."
#: editor/editor_node.cpp
msgid "Save Scene"
-msgstr ""
+msgstr "Salvează Scena"
#: editor/editor_node.cpp
msgid "Save all Scenes"
-msgstr ""
+msgstr "Salvează toate Scenele"
#: editor/editor_node.cpp
msgid "Close Scene"
-msgstr ""
+msgstr "ÃŽnchide Scena"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Open Recent"
-msgstr ""
+msgstr "Deschide Recente"
#: editor/editor_node.cpp
msgid "Convert To..."
-msgstr ""
+msgstr "Convertește În..."
#: editor/editor_node.cpp
msgid "MeshLibrary..."
-msgstr ""
+msgstr "Librărie_de_Structuri..."
#: editor/editor_node.cpp
msgid "TileSet..."
-msgstr ""
+msgstr "Set_de_Plăci..."
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
msgid "Undo"
-msgstr ""
+msgstr "Revenire"
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp
msgid "Redo"
-msgstr ""
+msgstr "Reîntoarcere"
#: editor/editor_node.cpp
msgid "Revert Scene"
-msgstr ""
+msgstr "Restabilește Scena"
#: editor/editor_node.cpp
msgid "Miscellaneous project or scene-wide tools."
-msgstr ""
+msgstr "Proiect Divers sau unelte pentru scenă."
#: editor/editor_node.cpp
msgid "Project"
-msgstr ""
+msgstr "Proiect"
#: editor/editor_node.cpp
msgid "Project Settings"
-msgstr ""
+msgstr "Setări ale Proiectului"
#: editor/editor_node.cpp
msgid "Run Script"
-msgstr ""
+msgstr "Execută Scriptul"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
-msgstr ""
+msgstr "Exportare"
#: editor/editor_node.cpp
msgid "Tools"
-msgstr ""
+msgstr "Unelte"
#: editor/editor_node.cpp
msgid "Quit to Project List"
-msgstr ""
+msgstr "ÃŽnchide spre Lista Proiectului"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Debug"
-msgstr ""
+msgstr "Depanare"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
-msgstr ""
+msgstr "Lansează cu Depanare la Distanță"
#: editor/editor_node.cpp
msgid ""
"When exporting or deploying, the resulting executable will attempt to "
"connect to the IP of this computer in order to be debugged."
msgstr ""
+"Când exporți sau lansezi, executabilul rezultat va încerca să se conecteze "
+"la IP-ul acestui computer pentru a putea fi depanat."
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr ""
+msgstr "Mini Lansare cu Rețea FS"
#: editor/editor_node.cpp
msgid ""
@@ -1973,30 +1990,39 @@ msgid ""
"On Android, deploy will use the USB cable for faster performance. This "
"option speeds up testing for games with a large footprint."
msgstr ""
+"Când această opțiune este activată, exportarea sau lansarea va produce un "
+"executabil minimal.\n"
+"Sistemul de fișiere va fi furnizat de la proiect la editor prin rețea.\n"
+"Pe Android, lansarea va folosi cablul USB pentru performanță mai rapidă. "
+"Această opțiune accelerează testarea jocurilor cu o marime substanțială."
#: editor/editor_node.cpp
msgid "Visible Collision Shapes"
-msgstr ""
+msgstr "Forme de Coliziune Vizibile"
#: editor/editor_node.cpp
msgid ""
"Collision shapes and raycast nodes (for 2D and 3D) will be visible on the "
"running game if this option is turned on."
msgstr ""
+"Formele de coliziune si nodurile raycast (pentru 2D și 3D) vor fi vizibile "
+"când jocul rulează dacă această opțiune este activată."
#: editor/editor_node.cpp
msgid "Visible Navigation"
-msgstr ""
+msgstr "Navigare Vizibilă"
#: editor/editor_node.cpp
msgid ""
"Navigation meshes and polygons will be visible on the running game if this "
"option is turned on."
msgstr ""
+"Structurile de navigare și poligoanele vor fi vizibile când jocul rulează "
+"dacă această opțiune este activată."
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr ""
+msgstr "Sincronizează Modificările Scenei"
#: editor/editor_node.cpp
msgid ""
@@ -2005,10 +2031,14 @@ msgid ""
"When used remotely on a device, this is more efficient with network "
"filesystem."
msgstr ""
+"Când această opțiune este activată, orice modificare facută în scenă din "
+"editor va fi replicată în jocul care rulează.\n"
+"Când această opțiune este folosită de la distanță pe un dispozitiv, este "
+"mult mai eficient dacă este folosit un sistem de fișiere în rețea."
#: editor/editor_node.cpp
msgid "Sync Script Changes"
-msgstr ""
+msgstr "Sincronizează Modificările Scriptului"
#: editor/editor_node.cpp
msgid ""
@@ -2017,844 +2047,861 @@ msgid ""
"When used remotely on a device, this is more efficient with network "
"filesystem."
msgstr ""
+"Când această opțiune este activată, orice script salvat ulterior va fi "
+"reîncărcat în jocul care rulează.\n"
+"Când această opțiune este folosită de la distanță pe un dispozitiv, este "
+"mult mai eficient dacă este folosit un sistem de fișiere în rețea."
#: editor/editor_node.cpp
msgid "Editor"
-msgstr ""
+msgstr "Editor"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
-msgstr ""
+msgstr "Setări ale Editorului"
#: editor/editor_node.cpp
msgid "Editor Layout"
-msgstr ""
+msgstr "Schema Editorului"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
-msgstr ""
+msgstr "Comută în Ecran Complet"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Manage Export Templates"
-msgstr ""
+msgstr "Administrează Șabloanele de Export"
#: editor/editor_node.cpp
msgid "Help"
-msgstr ""
+msgstr "Ajutor"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Classes"
-msgstr ""
+msgstr "Clase"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
#: editor/plugins/shader_editor_plugin.cpp editor/project_settings_editor.cpp
msgid "Search"
-msgstr ""
+msgstr "Căutare"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Online Docs"
-msgstr ""
+msgstr "Documentație Online"
#: editor/editor_node.cpp
msgid "Q&A"
-msgstr ""
+msgstr "Întrebări și Răspunsuri"
#: editor/editor_node.cpp
msgid "Issue Tracker"
-msgstr ""
+msgstr "Agent de Monitorizare al Problemelor"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
-msgstr ""
+msgstr "Comunitate"
#: editor/editor_node.cpp
msgid "About"
-msgstr ""
+msgstr "Despre"
#: editor/editor_node.cpp
msgid "Play the project."
-msgstr ""
+msgstr "Rulează proiectul."
#: editor/editor_node.cpp
msgid "Play"
-msgstr ""
+msgstr "Rulează"
#: editor/editor_node.cpp
msgid "Pause the scene"
-msgstr ""
+msgstr "ÃŽntrerupe scena"
#: editor/editor_node.cpp
msgid "Pause Scene"
-msgstr ""
+msgstr "Întrerupere Scenă"
#: editor/editor_node.cpp
msgid "Stop the scene."
-msgstr ""
+msgstr "Oprește scena."
#: editor/editor_node.cpp
msgid "Stop"
-msgstr ""
+msgstr "Oprește"
#: editor/editor_node.cpp
msgid "Play the edited scene."
-msgstr ""
+msgstr "Rulează scena editată."
#: editor/editor_node.cpp
msgid "Play Scene"
-msgstr ""
+msgstr "Rulează Scena"
#: editor/editor_node.cpp
msgid "Play custom scene"
-msgstr ""
+msgstr "Rulează scena personalizată"
#: editor/editor_node.cpp
msgid "Play Custom Scene"
-msgstr ""
+msgstr "Rulează Scena Personalizată"
#: editor/editor_node.cpp
msgid "Spins when the editor window repaints!"
-msgstr ""
+msgstr "Se rotește când ferestra editorului se recolorează!"
#: editor/editor_node.cpp
msgid "Update Always"
-msgstr ""
+msgstr "Actualizează Întotdeauna"
#: editor/editor_node.cpp
msgid "Update Changes"
-msgstr ""
+msgstr "Modificări ale Actualizării"
#: editor/editor_node.cpp
msgid "Disable Update Spinner"
-msgstr ""
+msgstr "Dezactivează Cercul de Actualizare"
#: editor/editor_node.cpp
msgid "Inspector"
-msgstr ""
+msgstr "Inspector"
#: editor/editor_node.cpp
msgid "Create a new resource in memory and edit it."
-msgstr ""
+msgstr "Creează o nouă resursă în memorie și editeaz-o."
#: editor/editor_node.cpp
msgid "Load an existing resource from disk and edit it."
-msgstr ""
+msgstr "Încarcă o resursă existentă de pe disc si editeaz-o."
#: editor/editor_node.cpp
msgid "Save the currently edited resource."
-msgstr ""
+msgstr "Salvează resursa editată curentă."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Save As..."
-msgstr ""
+msgstr "Salvează Ca..."
#: editor/editor_node.cpp
msgid "Go to the previous edited object in history."
-msgstr ""
+msgstr "Mergi la un obiect din istoric editat anterior."
#: editor/editor_node.cpp
msgid "Go to the next edited object in history."
-msgstr ""
+msgstr "Mergi la următorul obiect editat din istoric."
#: editor/editor_node.cpp
msgid "History of recently edited objects."
-msgstr ""
+msgstr "Istoricul obiectelor editate recent."
#: editor/editor_node.cpp
msgid "Object properties."
-msgstr ""
+msgstr "Proprietățile obiectului."
#: editor/editor_node.cpp
msgid "Changes may be lost!"
-msgstr ""
+msgstr "Modificările pot fi pierdute!"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Import"
-msgstr ""
+msgstr "Importă"
#: editor/editor_node.cpp
msgid "Node"
-msgstr ""
+msgstr "Nod"
#: editor/editor_node.cpp
msgid "FileSystem"
-msgstr ""
+msgstr "Sistemul De Fișiere"
#: editor/editor_node.cpp
msgid "Output"
-msgstr ""
+msgstr "Ieșire"
#: editor/editor_node.cpp
msgid "Don't Save"
-msgstr ""
+msgstr "Nu Salva"
#: editor/editor_node.cpp
msgid "Import Templates From ZIP File"
-msgstr ""
+msgstr "Importă Șabloane Dintr-o Arhivă ZIP"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export Project"
-msgstr ""
+msgstr "Exportă Proiectul"
#: editor/editor_node.cpp
msgid "Export Library"
-msgstr ""
+msgstr "Exportă Librăria"
#: editor/editor_node.cpp
msgid "Merge With Existing"
-msgstr ""
+msgstr "Contopește Cu Existentul"
#: editor/editor_node.cpp
msgid "Password:"
-msgstr ""
+msgstr "Parola:"
#: editor/editor_node.cpp
msgid "Open & Run a Script"
-msgstr ""
+msgstr "Deschide și Execută un Script"
#: editor/editor_node.cpp
msgid "New Inherited"
-msgstr ""
+msgstr "Derivare Nouă"
#: editor/editor_node.cpp
msgid "Load Errors"
-msgstr ""
+msgstr "Încarcă Erorile"
#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
msgid "Select"
-msgstr ""
+msgstr "Selectează"
#: editor/editor_node.cpp
msgid "Open 2D Editor"
-msgstr ""
+msgstr "Deschide Editorul 2D"
#: editor/editor_node.cpp
msgid "Open 3D Editor"
-msgstr ""
+msgstr "Deschide Editorul 3D"
#: editor/editor_node.cpp
msgid "Open Script Editor"
-msgstr ""
+msgstr "Deschide Editorul de Scripturi"
#: editor/editor_node.cpp editor/project_manager.cpp
msgid "Open Asset Library"
-msgstr ""
+msgstr "Deschide Librăria de Asseturi"
#: editor/editor_node.cpp
msgid "Open the next Editor"
-msgstr ""
+msgstr "Deschide Editorul următor"
#: editor/editor_node.cpp
msgid "Open the previous Editor"
-msgstr ""
+msgstr "Deschide Editorul anterior"
#: editor/editor_plugin.cpp
msgid "Creating Mesh Previews"
-msgstr ""
+msgstr "Se creează Previzualizările Mesh-ului"
#: editor/editor_plugin.cpp
msgid "Thumbnail..."
-msgstr ""
+msgstr "Miniatură..."
#: editor/editor_plugin_settings.cpp
msgid "Installed Plugins:"
-msgstr ""
+msgstr "Pluginuri instalate:"
#: editor/editor_plugin_settings.cpp
msgid "Update"
-msgstr ""
+msgstr "Actualizare"
#: editor/editor_plugin_settings.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Version:"
-msgstr ""
+msgstr "Versiune:"
#: editor/editor_plugin_settings.cpp
msgid "Author:"
-msgstr ""
+msgstr "Autor:"
#: editor/editor_plugin_settings.cpp
msgid "Status:"
-msgstr ""
+msgstr "Stare:"
#: editor/editor_profiler.cpp
msgid "Stop Profiling"
-msgstr ""
+msgstr "Oprește Profilarea"
#: editor/editor_profiler.cpp
msgid "Start Profiling"
-msgstr ""
+msgstr "Pornește Profilarea"
#: editor/editor_profiler.cpp
msgid "Measure:"
-msgstr ""
+msgstr "Măsura:"
#: editor/editor_profiler.cpp
msgid "Frame Time (sec)"
-msgstr ""
+msgstr "Timpul Cadrului (sec)"
#: editor/editor_profiler.cpp
msgid "Average Time (sec)"
-msgstr ""
+msgstr "Media Timpului (sec)"
#: editor/editor_profiler.cpp
msgid "Frame %"
-msgstr ""
+msgstr "Cadru %"
#: editor/editor_profiler.cpp
msgid "Physics Frame %"
-msgstr ""
+msgstr "Cadru Fizic %"
#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
msgid "Time:"
-msgstr ""
+msgstr "Timp:"
#: editor/editor_profiler.cpp
msgid "Inclusive"
-msgstr ""
+msgstr "Inclusiv"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr ""
+msgstr "Propriu"
#: editor/editor_profiler.cpp
msgid "Frame #:"
-msgstr ""
+msgstr "Cadru #:"
#: editor/editor_profiler.cpp
msgid "Time"
-msgstr ""
+msgstr "Timp"
#: editor/editor_profiler.cpp
msgid "Calls"
-msgstr ""
+msgstr "Apeluri"
#: editor/editor_run_native.cpp
msgid "Select device from the list"
-msgstr ""
+msgstr "Selectează un dispozitiv din listă"
#: editor/editor_run_native.cpp
msgid ""
"No runnable export preset found for this platform.\n"
"Please add a runnable preset in the export menu."
msgstr ""
+"Nu a fost găsită nicio presetare de export care să poată rula pentru această "
+"platformă.\n"
+"Te rog adaugă o presetare de rulare în meniul pentru export."
#: editor/editor_run_script.cpp
msgid "Write your logic in the _run() method."
-msgstr ""
+msgstr "Scrie logica programului în metoda _run()."
#: editor/editor_run_script.cpp
msgid "There is an edited scene already."
-msgstr ""
+msgstr "Acolo este o scenă deja editată."
#: editor/editor_run_script.cpp
msgid "Couldn't instance script:"
-msgstr ""
+msgstr "Nu s-a putut inițializa scriptul:"
#: editor/editor_run_script.cpp
msgid "Did you forget the 'tool' keyword?"
-msgstr ""
+msgstr "Ai uitat cumva cuvântul 'unealtă'?"
#: editor/editor_run_script.cpp
msgid "Couldn't run script:"
-msgstr ""
+msgstr "Nu a putut fi executat scriptul:"
#: editor/editor_run_script.cpp
msgid "Did you forget the '_run' method?"
-msgstr ""
+msgstr "Ai uitat cumva metoda '_run' ?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
-msgstr ""
+msgstr "Implicit (Asemănător ca Editor)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
-msgstr ""
+msgstr "Selectează Nodul(rile) pentru Importare"
#: editor/editor_sub_scene.cpp
msgid "Scene Path:"
-msgstr ""
+msgstr "Calea Scenei:"
#: editor/editor_sub_scene.cpp
msgid "Import From Node:"
-msgstr ""
+msgstr "Importă Din Nod:"
#: editor/export_template_manager.cpp
msgid "Re-Download"
-msgstr ""
+msgstr "Descarcă din nou"
#: editor/export_template_manager.cpp
msgid "Uninstall"
-msgstr ""
+msgstr "Dezinstalează"
#: editor/export_template_manager.cpp
msgid "(Installed)"
-msgstr ""
+msgstr "(Instalat)"
#: editor/export_template_manager.cpp
msgid "Download"
-msgstr ""
+msgstr "Descarcă"
#: editor/export_template_manager.cpp
msgid "(Missing)"
-msgstr ""
+msgstr "(Lipsește)"
#: editor/export_template_manager.cpp
msgid "(Current)"
-msgstr ""
+msgstr "(Curent)"
#: editor/export_template_manager.cpp
msgid "Retrieving mirrors, please wait..."
-msgstr ""
+msgstr "Se recuperează oglinzile, te rog așteaptă..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
-msgstr ""
+msgstr "Elimini șablonul versiunea '%s'?"
#: editor/export_template_manager.cpp
msgid "Can't open export templates zip."
-msgstr ""
+msgstr "Nu se pot deschide șabloanele de export zip."
#: editor/export_template_manager.cpp
msgid "Invalid version.txt format inside templates."
-msgstr ""
+msgstr "Format nevalid versiune.txt în șabloane."
#: editor/export_template_manager.cpp
msgid "No version.txt found inside templates."
-msgstr ""
+msgstr "Nu s-a găsit versiune.txt în șabloane."
#: editor/export_template_manager.cpp
msgid "Error creating path for templates:"
-msgstr ""
+msgstr "Eroare la crearea căii pentru șabloane:"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
-msgstr ""
+msgstr "Se extrag Șabloanele de Export"
#: editor/export_template_manager.cpp
msgid "Importing:"
-msgstr ""
+msgstr "Se importă:"
#: editor/export_template_manager.cpp
msgid ""
"No download links found for this version. Direct download is only available "
"for official releases."
msgstr ""
+"Niciun link pentru descărcare nu a fost găsit pentru această versiune. "
+"Descărcarea directă este disponibilă numai pentru lansări oficiale."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve."
-msgstr ""
+msgstr "Nu se poate rezolva."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't connect."
-msgstr ""
+msgstr "Nu se poate face conexiunea."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "No response."
-msgstr ""
+msgstr "Niciun răspuns."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request Failed."
-msgstr ""
+msgstr "Cerere Eșuată."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Redirect Loop."
-msgstr ""
+msgstr "Buclă de Redirecționare."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Failed:"
-msgstr ""
+msgstr "A Eșuat:"
#: editor/export_template_manager.cpp
msgid "Download Complete."
-msgstr ""
+msgstr "Descărcare Completă."
#: editor/export_template_manager.cpp
msgid "Error requesting url: "
-msgstr ""
+msgstr "Eroare la solicitarea URL: "
#: editor/export_template_manager.cpp
msgid "Connecting to Mirror..."
-msgstr ""
+msgstr "Se conectează la Oglinda..."
#: editor/export_template_manager.cpp
msgid "Disconnected"
-msgstr ""
+msgstr "Deconectat"
#: editor/export_template_manager.cpp
msgid "Resolving"
-msgstr ""
+msgstr "Se Soluționează"
#: editor/export_template_manager.cpp
msgid "Can't Resolve"
-msgstr ""
+msgstr "Nu se poate Soluționa"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Connecting..."
-msgstr ""
+msgstr "Conectare..."
#: editor/export_template_manager.cpp
msgid "Can't Connect"
-msgstr ""
+msgstr "Nu se poate Conecta"
#: editor/export_template_manager.cpp
msgid "Connected"
-msgstr ""
+msgstr "Conectat"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Requesting..."
-msgstr ""
+msgstr "Se Solicită..."
#: editor/export_template_manager.cpp
msgid "Downloading"
-msgstr ""
+msgstr "Se Descarcă"
#: editor/export_template_manager.cpp
msgid "Connection Error"
-msgstr ""
+msgstr "Eroare de Conexiune"
#: editor/export_template_manager.cpp
msgid "SSL Handshake Error"
-msgstr ""
+msgstr "Eroare SSL Handshake"
#: editor/export_template_manager.cpp
msgid "Current Version:"
-msgstr ""
+msgstr "Versiune Curentă:"
#: editor/export_template_manager.cpp
msgid "Installed Versions:"
-msgstr ""
+msgstr "Versiuni Instalate:"
#: editor/export_template_manager.cpp
msgid "Install From File"
-msgstr ""
+msgstr "Instalează Din Fișier"
#: editor/export_template_manager.cpp
msgid "Remove Template"
-msgstr ""
+msgstr "Elimină Șablon"
#: editor/export_template_manager.cpp
msgid "Select template file"
-msgstr ""
+msgstr "Selectează fișierul șablon"
#: editor/export_template_manager.cpp
msgid "Export Template Manager"
-msgstr ""
+msgstr "Exportă Managerul de Șabloane"
#: editor/export_template_manager.cpp
msgid "Download Templates"
-msgstr ""
+msgstr "Descarcă Șabloane"
#: editor/export_template_manager.cpp
msgid "Select mirror from list: "
-msgstr ""
+msgstr "Selectează oglinda din listă: "
#: editor/file_type_cache.cpp
msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
msgstr ""
+"Nu se poate deschide file_type_cache.cch pentru scriere, nu se salvează "
+"fișierul tip cache!"
#: editor/filesystem_dock.cpp
msgid "Cannot navigate to '%s' as it has not been found in the file system!"
msgstr ""
+"Nu se poate naviga către '%s' pentru că nu a fost găsit în sistemul de "
+"fișiere!"
#: editor/filesystem_dock.cpp
msgid "View items as a grid of thumbnails"
-msgstr ""
+msgstr "Vizualizează articolele ca și o grilă de miniaturi"
#: editor/filesystem_dock.cpp
msgid "View items as a list"
-msgstr ""
+msgstr "Vizualizează articolele ca și o listă"
#: editor/filesystem_dock.cpp
msgid "Status: Import of file failed. Please fix file and reimport manually."
msgstr ""
+"Stare: Importarea fișierului eșuată. Te rog repară fișierul și reimportă "
+"manual."
#: editor/filesystem_dock.cpp
msgid "Cannot move/rename resources root."
-msgstr ""
+msgstr "Nu se poate muta/redenumi rădăcina resurselor."
#: editor/filesystem_dock.cpp
msgid "Cannot move a folder into itself."
-msgstr ""
+msgstr "Nu se poate muta un director în el însuși."
#: editor/filesystem_dock.cpp
msgid "Error moving:"
-msgstr ""
+msgstr "Eroare mutând:"
#: editor/filesystem_dock.cpp
msgid "Error duplicating:"
-msgstr ""
+msgstr "Eroare duplicând:"
#: editor/filesystem_dock.cpp
msgid "Unable to update dependencies:"
-msgstr ""
+msgstr "Imposibil de actualizat dependințele:"
#: editor/filesystem_dock.cpp
msgid "No name provided"
-msgstr ""
+msgstr "Niciun nume furnizat"
#: editor/filesystem_dock.cpp
msgid "Provided name contains invalid characters"
-msgstr ""
+msgstr "Numele furnizat conține caractere nevalide"
#: editor/filesystem_dock.cpp
msgid "No name provided."
-msgstr ""
+msgstr "Niciun nume furnizat."
#: editor/filesystem_dock.cpp
msgid "Name contains invalid characters."
-msgstr ""
+msgstr "Numele furnizat conține caractere nevalide."
#: editor/filesystem_dock.cpp
msgid "A file or folder with this name already exists."
-msgstr ""
+msgstr "Un fișier sau un director cu acest nume există deja."
#: editor/filesystem_dock.cpp
msgid "Renaming file:"
-msgstr ""
+msgstr "Redenumind fișierul:"
#: editor/filesystem_dock.cpp
msgid "Renaming folder:"
-msgstr ""
+msgstr "Redenumind directorul:"
#: editor/filesystem_dock.cpp
msgid "Duplicating file:"
-msgstr ""
+msgstr "Duplicând fișierul:"
#: editor/filesystem_dock.cpp
msgid "Duplicating folder:"
-msgstr ""
+msgstr "Duplicând directorul:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
-msgstr ""
+msgstr "Extinde toate"
#: editor/filesystem_dock.cpp
msgid "Collapse all"
-msgstr ""
+msgstr "Restrânge toate"
#: editor/filesystem_dock.cpp
msgid "Rename..."
-msgstr ""
+msgstr "Redenumește..."
#: editor/filesystem_dock.cpp
msgid "Move To..."
-msgstr ""
+msgstr "Mută În..."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
-msgstr ""
+msgstr "Deschide Scena(ele)"
#: editor/filesystem_dock.cpp
msgid "Instance"
-msgstr ""
+msgstr "Instanță"
#: editor/filesystem_dock.cpp
msgid "Edit Dependencies..."
-msgstr ""
+msgstr "Editează Dependințele..."
#: editor/filesystem_dock.cpp
msgid "View Owners..."
-msgstr ""
+msgstr "Vizualizează Proprietarii..."
#: editor/filesystem_dock.cpp
msgid "Duplicate..."
-msgstr ""
+msgstr "Duplicați..."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
-msgstr ""
+msgstr "Directorul Anterior"
#: editor/filesystem_dock.cpp
msgid "Next Directory"
-msgstr ""
+msgstr "Directorul Urmator"
#: editor/filesystem_dock.cpp
msgid "Re-Scan Filesystem"
-msgstr ""
+msgstr "Rescanează Sistemul de Fișiere"
#: editor/filesystem_dock.cpp
msgid "Toggle folder status as Favorite"
-msgstr ""
+msgstr "Marchează statutul directorului ca Favorit"
#: editor/filesystem_dock.cpp
msgid "Instance the selected scene(s) as child of the selected node."
-msgstr ""
+msgstr "Instanțiază scena(ele) selectată ca un copil al nodului selectat."
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
"Please Wait..."
msgstr ""
+"Se Scanează Fișierele,\n"
+"Te Rog Așteaptă..."
#: editor/filesystem_dock.cpp
msgid "Move"
-msgstr ""
+msgstr "Mută"
#: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Rename"
-msgstr ""
+msgstr "Redenumește"
#: editor/groups_editor.cpp
msgid "Add to Group"
-msgstr ""
+msgstr "Adaugă în Grup"
#: editor/groups_editor.cpp
msgid "Remove from Group"
-msgstr ""
+msgstr "Elimină din Grup"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Single Scene"
-msgstr ""
+msgstr "Importă ca Scenă Simplă"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Animations"
-msgstr ""
+msgstr "Importă cu Animații Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
-msgstr ""
+msgstr "Importă cu Materiale Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects"
-msgstr ""
+msgstr "Importă cu Obiecte Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials"
-msgstr ""
+msgstr "Importă cu Obiecte+Materiale Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Animations"
-msgstr ""
+msgstr "Importă cu Obiecte+Animații Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials+Animations"
-msgstr ""
+msgstr "Importă cu Materiale+Animații Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials+Animations"
-msgstr ""
+msgstr "Importă cu Obiecte+Materiale+Animații Separate"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes"
-msgstr ""
+msgstr "Importă ca Scene Multiple"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes+Materials"
-msgstr ""
+msgstr "Importă ca Scene+Materiale Multiple"
#: editor/import/resource_importer_scene.cpp
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import Scene"
-msgstr ""
+msgstr "Importă Scena"
#: editor/import/resource_importer_scene.cpp
msgid "Importing Scene..."
-msgstr ""
+msgstr "Se Importa Scena..."
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
-msgstr ""
+msgstr "Se Genereaza Lightmaps"
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr ""
+msgstr "Se Generează pentru Mesh: "
#: editor/import/resource_importer_scene.cpp
msgid "Running Custom Script..."
-msgstr ""
+msgstr "Se Execută un Script Personalizat..."
#: editor/import/resource_importer_scene.cpp
msgid "Couldn't load post-import script:"
-msgstr ""
+msgstr "Nu s-a putut încărca scriptul post-importare:"
#: editor/import/resource_importer_scene.cpp
msgid "Invalid/broken script for post-import (check console):"
-msgstr ""
+msgstr "Script nevalid/nefuncțional pentru post-importare (vezi consola):"
#: editor/import/resource_importer_scene.cpp
msgid "Error running post-import script:"
-msgstr ""
+msgstr "Eroare la executarea scripyului post-importare:"
#: editor/import/resource_importer_scene.cpp
msgid "Saving..."
-msgstr ""
+msgstr "Se Salvează..."
#: editor/import_dock.cpp
msgid "Set as Default for '%s'"
-msgstr ""
+msgstr "Setează ca Implicit pentru '%s'"
#: editor/import_dock.cpp
msgid "Clear Default for '%s'"
-msgstr ""
+msgstr "Curăță setarea Implicită pentru '%s'"
#: editor/import_dock.cpp
msgid " Files"
-msgstr ""
+msgstr " Fișiere"
#: editor/import_dock.cpp
msgid "Import As:"
-msgstr ""
+msgstr "Importă Ca:"
#: editor/import_dock.cpp editor/property_editor.cpp
msgid "Preset..."
-msgstr ""
+msgstr "Presetare..."
#: editor/import_dock.cpp
msgid "Reimport"
-msgstr ""
+msgstr "Reimportă"
#: editor/multi_node_edit.cpp
msgid "MultiNode Set"
-msgstr ""
+msgstr "Set MultiNod"
#: editor/node_dock.cpp
msgid "Groups"
-msgstr ""
+msgstr "Grupuri"
#: editor/node_dock.cpp
msgid "Select a Node to edit Signals and Groups."
-msgstr ""
+msgstr "Selectează un Nod pentru a edita Semnalele și Grupurile."
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Poly"
-msgstr ""
+msgstr "Crează Poligon"
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/collision_polygon_editor_plugin.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit Poly"
-msgstr ""
+msgstr "Editează Poligon"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Insert Point"
-msgstr ""
+msgstr "Inserează Punct"
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/collision_polygon_editor_plugin.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit Poly (Remove Point)"
-msgstr ""
+msgstr "Editează Poligon (Elimină Punct)"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Remove Poly And Point"
-msgstr ""
+msgstr "Elimină Poligon Și Punct"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Create a new polygon from scratch"
-msgstr ""
+msgstr "Crează un nou poligon de la zero"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid ""
@@ -2863,522 +2910,526 @@ msgid ""
"Ctrl+LMB: Split Segment.\n"
"RMB: Erase Point."
msgstr ""
+"Editează poligon existent:\n"
+"LMB: Mută Punct.\n"
+"Ctrl+LMB: Despică Segment.\n"
+"RMB: Șterge Punct."
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Delete points"
-msgstr ""
+msgstr "Șterge puncte"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Toggle Autoplay"
-msgstr ""
+msgstr "Comutează Auto-Execuție"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Animation Name:"
-msgstr ""
+msgstr "Nume Nou Animație:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Anim"
-msgstr ""
+msgstr "Anim Nouă"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Animation Name:"
-msgstr ""
+msgstr "Schimbă Numele Animației:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Delete Animation?"
-msgstr ""
+msgstr "Ștergi Animația?"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Remove Animation"
-msgstr ""
+msgstr "Elimină Animația"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Invalid animation name!"
-msgstr ""
+msgstr "EROARE: Nume animație nevalid!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Animation name already exists!"
-msgstr ""
+msgstr "EROARE: Numele animației există deja!"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Rename Animation"
-msgstr ""
+msgstr "Redenumește Animația"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Animation"
-msgstr ""
+msgstr "Adaugă Animația"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Next Changed"
-msgstr ""
+msgstr "Amestecă Următoarea Schimbare"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Blend Time"
-msgstr ""
+msgstr "Schimbă Timpul Amestecului"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load Animation"
-msgstr ""
+msgstr "Încarcă Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Duplicate Animation"
-msgstr ""
+msgstr "Duplicare Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to copy!"
-msgstr ""
+msgstr "EROARE: Nicio copie a animației!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation resource on clipboard!"
-msgstr ""
+msgstr "EROARE: Nicio resursă de animație în clipboard!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Pasted Animation"
-msgstr ""
+msgstr "Animație Lipită"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Paste Animation"
-msgstr ""
+msgstr "Lipește Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to edit!"
-msgstr ""
+msgstr "EROARE: Nicio animație pentru editare!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from current pos. (A)"
-msgstr ""
+msgstr "Rulează animația selectată în sens invers de la poziția curentă. (A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from end. (Shift+A)"
-msgstr ""
+msgstr "Rulează animația selectată în sens invers de la sfârșit. (Shift+A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Stop animation playback. (S)"
-msgstr ""
+msgstr "Oprește rularea animației. (S)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from start. (Shift+D)"
-msgstr ""
+msgstr "Rulează animația selectată de la început. (Shift+D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from current pos. (D)"
-msgstr ""
+msgstr "Rulează animația selectată de la poziția curentă. (D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation position (in seconds)."
-msgstr ""
+msgstr "Poziția animației (în secunde)."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Scale animation playback globally for the node."
-msgstr ""
+msgstr "Redimensionează rularea animației pentru nod."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create new animation in player."
-msgstr ""
+msgstr "Creează o nouă animație în player."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load animation from disk."
-msgstr ""
+msgstr "Încarcă animație de pe disc."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load an animation from disk."
-msgstr ""
+msgstr "Încarcă o animație de pe disc."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Save the current animation"
-msgstr ""
+msgstr "Salvează actuala animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Display list of animations in player."
-msgstr ""
+msgstr "Afișează o listă a animațiilor în player."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Autoplay on Load"
-msgstr ""
+msgstr "Auto-Execută la Încărcare"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
-msgstr ""
+msgstr "Editează Timpul de Amestecare al Țintei"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Tools"
-msgstr ""
+msgstr "Unelte Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Copy Animation"
-msgstr ""
+msgstr "Copiză Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Onion Skinning"
-msgstr ""
+msgstr "Onion Skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Enable Onion Skinning"
-msgstr ""
+msgstr "Activează Onion Skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Directions"
-msgstr ""
+msgstr "Direcții"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Past"
-msgstr ""
+msgstr "Trecut"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Future"
-msgstr ""
+msgstr "Viitor"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Depth"
-msgstr ""
+msgstr "Adâncime"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "1 step"
-msgstr ""
+msgstr "1 pas"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "2 steps"
-msgstr ""
+msgstr "2 pași"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "3 steps"
-msgstr ""
+msgstr "3 pași"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Differences Only"
-msgstr ""
+msgstr "Doar Diferențe"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Force White Modulate"
-msgstr ""
+msgstr "Forțează Modulare Albă"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Include Gizmos (3D)"
-msgstr ""
+msgstr "Include Gizmos (3D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create New Animation"
-msgstr ""
+msgstr "Creează Animație Nouă"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Name:"
-msgstr ""
+msgstr "Nume Animație:"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Error!"
-msgstr ""
+msgstr "Eroare!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Times:"
-msgstr ""
+msgstr "Timpi de Amestecare:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Next (Auto Queue):"
-msgstr ""
+msgstr "Următorul (Rând Automat):"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Cross-Animation Blend Times"
-msgstr ""
+msgstr "Timpi de Amestecare Cross-Animație"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Animation"
-msgstr ""
+msgstr "Animație"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "New name:"
-msgstr ""
+msgstr "Nume nou:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Filters"
-msgstr ""
+msgstr "Editează Filtrele"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Scale:"
-msgstr ""
+msgstr "Dimensiune:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Fade In (s):"
-msgstr ""
+msgstr "Estompează (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Fade Out (s):"
-msgstr ""
+msgstr "Reliefează (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend"
-msgstr ""
+msgstr "Amestec"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix"
-msgstr ""
+msgstr "Mix"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Auto Restart:"
-msgstr ""
+msgstr "Restartare Automată:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Restart (s):"
-msgstr ""
+msgstr "Restartare (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Random Restart (s):"
-msgstr ""
+msgstr "Restartare Aleatorie (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Start!"
-msgstr ""
+msgstr "Start!"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Amount:"
-msgstr ""
+msgstr "Cantitate:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend:"
-msgstr ""
+msgstr "Amestec:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend 0:"
-msgstr ""
+msgstr "Amestec 0:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend 1:"
-msgstr ""
+msgstr "Amestec 1:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "X-Fade Time (s):"
-msgstr ""
+msgstr "Timp X-Decolorare (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Current:"
-msgstr ""
+msgstr "Curent:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Add Input"
-msgstr ""
+msgstr "Adaugă Intrare(Input)"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Clear Auto-Advance"
-msgstr ""
+msgstr "Curăță Auto-Avansarea"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Set Auto-Advance"
-msgstr ""
+msgstr "Setează Auto-Avansare"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Delete Input"
-msgstr ""
+msgstr "Șterge Intrare(Input)"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation tree is valid."
-msgstr ""
+msgstr "Arborele Animației este valid."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation tree is invalid."
-msgstr ""
+msgstr "Arborele Animației este nevalid."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation Node"
-msgstr ""
+msgstr "Nod de Animație"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "OneShot Node"
-msgstr ""
+msgstr "Nod OneShot"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix Node"
-msgstr ""
+msgstr "Nod de Amestecare"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend2 Node"
-msgstr ""
+msgstr "Nod Amestec2"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend3 Node"
-msgstr ""
+msgstr "Nod Amestec3"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend4 Node"
-msgstr ""
+msgstr "Nod Amestec4"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeScale Node"
-msgstr ""
+msgstr "Nod DimensiuneTimp"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeSeek Node"
-msgstr ""
+msgstr "Nod CăutareTimp"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Transition Node"
-msgstr ""
+msgstr "Nod Tranziție"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Import Animations..."
-msgstr ""
+msgstr "Importă Animații..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
-msgstr ""
+msgstr "Editează Filtrele Nodurilor"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Filters..."
-msgstr ""
+msgstr "Filtre..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
-msgstr ""
+msgstr "ArboreAnimație"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
-msgstr ""
+msgstr "Gratuit"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Contents:"
-msgstr ""
+msgstr "Conținut:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "View Files"
-msgstr ""
+msgstr "Vizualizează Fișierele"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve hostname:"
-msgstr ""
+msgstr "Nu se poate rezolva numele gazdei:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Connection error, please try again."
-msgstr ""
+msgstr "Eroare la conectare, te rog încearcă din nou."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't connect to host:"
-msgstr ""
+msgstr "Nu se poate conecta la gazda:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "No response from host:"
-msgstr ""
+msgstr "Nciun răspuns de la gazda:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request failed, return code:"
-msgstr ""
+msgstr "Cerere eșuată, cod returnat:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request failed, too many redirects"
-msgstr ""
+msgstr "Cerere eșuată, prea multe redirecționări"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Bad download hash, assuming file has been tampered with."
-msgstr ""
+msgstr "Hash eronat de descărcare, se presupune că fișierul este falsificat."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Expected:"
-msgstr ""
+msgstr "Așteptat:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Got:"
-msgstr ""
+msgstr "Primit:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Failed sha256 hash check"
-msgstr ""
+msgstr "Verificare hash sha256 eșuată"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Asset Download Error:"
-msgstr ""
+msgstr "Eroare la Descărcarea Asset-ului:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Fetching:"
-msgstr ""
+msgstr "Se Preia(u):"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Resolving..."
-msgstr ""
+msgstr "Se Rezolvă..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
-msgstr ""
+msgstr "Eroare la solicitare"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Idle"
-msgstr ""
+msgstr "Inactiv"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Retry"
-msgstr ""
+msgstr "Reîncearcă"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Download Error"
-msgstr ""
+msgstr "Eroare Descărcare"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Download for this asset is already in progress!"
-msgstr ""
+msgstr "Descărcarea acestui asset rulează deja!"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "first"
-msgstr ""
+msgstr "primul"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "prev"
-msgstr ""
+msgstr "anterior"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "next"
-msgstr ""
+msgstr "următorul"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "last"
-msgstr ""
+msgstr "ultimul"
#: editor/plugins/asset_library_editor_plugin.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "All"
-msgstr ""
+msgstr "Toate"
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_settings_editor.cpp
msgid "Plugins"
-msgstr ""
+msgstr "Plugin-uri"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Sort:"
-msgstr ""
+msgstr "Sorare:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Reverse"
-msgstr ""
+msgstr "Revers"
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_settings_editor.cpp
msgid "Category:"
-msgstr ""
+msgstr "Categorie:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Site:"
-msgstr ""
+msgstr "Site:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Support..."
-msgstr ""
+msgstr "Suport..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
-msgstr ""
+msgstr "Oficial"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Testing"
-msgstr ""
+msgstr "Se Testează"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Assets ZIP File"
-msgstr ""
+msgstr "Fișier ZIP cu Asset-uri"
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid ""
@@ -3386,135 +3437,144 @@ msgid ""
"Save your scene (for images to be saved in the same dir), or pick a save "
"path from the BakedLightmap properties."
msgstr ""
+"Nu se poate determina p cale de salvare pentru imaginile lightmap.\n"
+"Salvează scena (imaginile vor fi salvate în acelasi director), sau alege o "
+"cale de salvare din proprietățile BakedLightmap."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid ""
"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake "
"Light' flag is on."
msgstr ""
+"Nicio structură pentru procesare. Asigură-te că acestea conțin un canal UV2 "
+"și că opțiunea 'Procesează Lumina' este pornită."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
msgstr ""
+"Crearea imaginilor lightmap eșuată, asigură-te că poate fi scrisă calea spre "
+"ele."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Bake Lightmaps"
-msgstr ""
+msgstr "Procesează Lightmaps"
#: editor/plugins/camera_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Preview"
-msgstr ""
+msgstr "Previzualizare"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Configure Snap"
-msgstr ""
+msgstr "Configurare Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Offset:"
-msgstr ""
+msgstr "Compensare Grilă:"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid Step:"
-msgstr ""
+msgstr "Pas Grilă:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Offset:"
-msgstr ""
+msgstr "Compensare Rotație:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotation Step:"
-msgstr ""
+msgstr "Pas Rotație:"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Pivot"
-msgstr ""
+msgstr "Mută Pivot"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Action"
-msgstr ""
+msgstr "Acțiune de Mutare"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move vertical guide"
-msgstr ""
+msgstr "Mută ghidul vertical"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new vertical guide"
-msgstr ""
+msgstr "Creează un nou ghid vertical"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Remove vertical guide"
-msgstr ""
+msgstr "Elimină ghidul vertical"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move horizontal guide"
-msgstr ""
+msgstr "Mută ghidul orizontal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new horizontal guide"
-msgstr ""
+msgstr "Creează un nou ghid orizontal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Remove horizontal guide"
-msgstr ""
+msgstr "Elimină ghidul orizontal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new horizontal and vertical guides"
-msgstr ""
+msgstr "Creează ghizi noi orizontal și vertical"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Edit IK Chain"
-msgstr ""
+msgstr "Editează Lanț IK"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Edit CanvasItem"
-msgstr ""
+msgstr "Editează ObiectulPânză"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Anchors only"
-msgstr ""
+msgstr "Doar ancore"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change Anchors and Margins"
-msgstr ""
+msgstr "Modifică Ancorele și Limitele"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change Anchors"
-msgstr ""
+msgstr "Modifică Ancorele"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Paste Pose"
-msgstr ""
+msgstr "Lipește Postura"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Select Mode"
-msgstr ""
+msgstr "Mod Selectare"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Drag: Rotate"
-msgstr ""
+msgstr "Trage: Rotire"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+Drag: Move"
-msgstr ""
+msgstr "Alt+Trage: Mutare"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)."
msgstr ""
+"Apasă 'v' pentru a Schimba Pivotul, 'Shift+v' pentru a Trage Pivotul (în "
+"timpul mișcării)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+RMB: Depth list selection"
-msgstr ""
+msgstr "Alt+RMB: Selecție adâncime listă"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
-msgstr ""
+msgstr "Mod Mutare"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotate Mode"
-msgstr ""
+msgstr "Mod Rotație"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3522,560 +3582,566 @@ msgid ""
"Show a list of all objects at the position clicked\n"
"(same as Alt+RMB in select mode)."
msgstr ""
+"Arată o listă a tuturor obiectelor la poziția clickului\n"
+"(similar cu Alt+RMB în modul selectare)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Click to change object's rotation pivot."
-msgstr ""
+msgstr "Click pentru a modifica pivotul de rotație al obiectului."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Pan Mode"
-msgstr ""
+msgstr "Mod ÃŽn Jur"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Toggles snapping"
-msgstr ""
+msgstr "Comutare snapping"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Snap"
-msgstr ""
+msgstr "Utilizează Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snapping options"
-msgstr ""
+msgstr "Opțiuni Snapping"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to grid"
-msgstr ""
+msgstr "Snap pe grilă"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Rotation Snap"
-msgstr ""
+msgstr "Folosește Rotația Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr ""
+msgstr "Configurare Snap..."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
-msgstr ""
+msgstr "Snap Relativ"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Pixel Snap"
-msgstr ""
+msgstr "Utilizează Pixel Snap"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Smart snapping"
-msgstr ""
+msgstr "Snapping inteligent"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to parent"
-msgstr ""
+msgstr "Snap către părinte"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
-msgstr ""
+msgstr "Snap către ancora nodului"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
-msgstr ""
+msgstr "Snap pe fețele nodului"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to other nodes"
-msgstr ""
+msgstr "Snap către alte noduri"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to guides"
-msgstr ""
+msgstr "Snap pe ghizi"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Lock the selected object in place (can't be moved)."
-msgstr ""
+msgstr "Imobilizează obiectul selectat (nu poate fi mișcat)."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Unlock the selected object (can be moved)."
-msgstr ""
+msgstr "Remobilizează obiectul selectat (poate fi mișcat)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Makes sure the object's children are not selectable."
-msgstr ""
+msgstr "Asigură-te că nu pot fi selectați copiii obiectului."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Restores the object's children's ability to be selected."
-msgstr ""
+msgstr "Restaurează abilitatea copiilor obiectului de a fi selectați."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Make Bones"
-msgstr ""
+msgstr "Creează Oase"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Clear Bones"
-msgstr ""
+msgstr "Curăță Oasele"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Bones"
-msgstr ""
+msgstr "Arată Oasele"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Make IK Chain"
-msgstr ""
+msgstr "Creează Lanț IK"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Clear IK Chain"
-msgstr ""
+msgstr "Curăță Lanțul IK"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View"
-msgstr ""
+msgstr "Perspectivă"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Show Grid"
-msgstr ""
+msgstr "Arată Grila"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Helpers"
-msgstr ""
+msgstr "Arată Asistenții"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Rulers"
-msgstr ""
+msgstr "Arată Riglele"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Guides"
-msgstr ""
+msgstr "Arată Ghizii"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Origin"
-msgstr ""
+msgstr "Arată Originea"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Viewport"
-msgstr ""
+msgstr "Arată Fereastra de Lucru"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
-msgstr ""
+msgstr "Centrează Selecția"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Frame Selection"
-msgstr ""
+msgstr "Încadrează în Ecran Selecția"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Layout"
-msgstr ""
+msgstr "Schemă"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Keys"
-msgstr ""
+msgstr "Inserează Note"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key"
-msgstr ""
+msgstr "Inserează Notă"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key (Existing Tracks)"
-msgstr ""
+msgstr "Inserează Notă (Melodii existente)"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Copy Pose"
-msgstr ""
+msgstr "Copiază Postura"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Clear Pose"
-msgstr ""
+msgstr "Curăță Postura"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Drag pivot from mouse position"
-msgstr ""
+msgstr "Trage pivotul de la poziția mouse-ului"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Set pivot at mouse position"
-msgstr ""
+msgstr "Setează pivotul la poziția mouse-ului"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
-msgstr ""
+msgstr "Multiplică pasul pe grilă cu 2"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Divide grid step by 2"
-msgstr ""
+msgstr "Împarte pasul pe grilă cu 2"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Add %s"
-msgstr ""
+msgstr "Adaugă %s"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Adding %s..."
-msgstr ""
+msgstr "Se adaugă %s..."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
-msgstr ""
+msgstr "Bine"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr ""
+msgstr "Nu se pot instanția noduri multiple fără o rădacină."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Create Node"
-msgstr ""
+msgstr "Creează Nod"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Error instancing scene from %s"
-msgstr ""
+msgstr "Eroare la instanțierea scenei din %s"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change default type"
-msgstr ""
+msgstr "Schimbă tipul implicit"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid ""
"Drag & drop + Shift : Add node as sibling\n"
"Drag & drop + Alt : Change node type"
msgstr ""
+"Trage & lasă + Shift: Adaugă nod ca și frate\n"
+"Trage & lasă + Shift: Schimbă tipul nodului"
#: editor/plugins/collision_polygon_editor_plugin.cpp
msgid "Create Poly3D"
-msgstr ""
+msgstr "Creează Poligon3D"
#: editor/plugins/collision_shape_2d_editor_plugin.cpp
msgid "Set Handle"
-msgstr ""
+msgstr "Setează Mâner"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Remove item %d?"
-msgstr ""
+msgstr "Elimini obiectul %d?"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Add Item"
-msgstr ""
+msgstr "Adaugă Obiect"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Remove Selected Item"
-msgstr ""
+msgstr "Elimină Obiectul Selectat"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import from Scene"
-msgstr ""
+msgstr "Importă din Scenă"
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Update from Scene"
-msgstr ""
+msgstr "Actualizează din Scenă"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat0"
-msgstr ""
+msgstr "Plat0"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat1"
-msgstr ""
+msgstr "Plat1"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease in"
-msgstr ""
+msgstr "Facilitare în"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease out"
-msgstr ""
+msgstr "Facilitare din"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Smoothstep"
-msgstr ""
+msgstr "PasOmogen"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Modify Curve Point"
-msgstr ""
+msgstr "Modifică Punctul Curbei"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Modify Curve Tangent"
-msgstr ""
+msgstr "Modifică Tangenta Curbei"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Load Curve Preset"
-msgstr ""
+msgstr "Încarcă Presetare a Curbei"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Add point"
-msgstr ""
+msgstr "Adaugă punct"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Remove point"
-msgstr ""
+msgstr "Elimină punct"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Left linear"
-msgstr ""
+msgstr "Stânga liniară"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Right linear"
-msgstr ""
+msgstr "Dreapta liniară"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Load preset"
-msgstr ""
+msgstr "Încarcă presetare"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Remove Curve Point"
-msgstr ""
+msgstr "Elimină Punctul Curbei"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Toggle Curve Linear Tangent"
-msgstr ""
+msgstr "Comută Tangenta Liniară a Curbei"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Hold Shift to edit tangents individually"
-msgstr ""
+msgstr "Ține apăsat Shift pentru a edita individual tangentele"
#: editor/plugins/gi_probe_editor_plugin.cpp
msgid "Bake GI Probe"
-msgstr ""
+msgstr "Procesează Sonda GI"
#: editor/plugins/gradient_editor_plugin.cpp
msgid "Add/Remove Color Ramp Point"
-msgstr ""
+msgstr "Adaugă/Elimină Punctul Rampei de Culori"
#: editor/plugins/gradient_editor_plugin.cpp
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Modify Color Ramp"
-msgstr ""
+msgstr "Modifică Rampa de Culori"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item %d"
-msgstr ""
+msgstr "Obiect %d"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Items"
-msgstr ""
+msgstr "Obiecte"
#: editor/plugins/item_list_editor_plugin.cpp
msgid "Item List Editor"
-msgstr ""
+msgstr "Editor Lista de Obiect"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid ""
"No OccluderPolygon2D resource on this node.\n"
"Create and assign one?"
msgstr ""
+"Nicio resursă OccluderPolygon2D în acest nod.\n"
+"Vrei să creezi și să atribui una?"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Occluder Polygon"
-msgstr ""
+msgstr "Creează Poligon de Ocluziune"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create a new polygon from scratch."
-msgstr ""
+msgstr "Creează un nou poligon de la zero."
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit existing polygon:"
-msgstr ""
+msgstr "Editează poligonul existent:"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "LMB: Move Point."
-msgstr ""
+msgstr "LMB: Mișcă Punctul."
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Ctrl+LMB: Split Segment."
-msgstr ""
+msgstr "Ctrl+LMB: Despică Segmentul."
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "RMB: Erase Point."
-msgstr ""
+msgstr "RMB: Șterge Punctul."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh is empty!"
-msgstr ""
+msgstr "Mesh-ul este gol!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Trimesh Body"
-msgstr ""
+msgstr "Creează un Corp Static Trimesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Convex Body"
-msgstr ""
+msgstr "Creează un Corp Static Convex"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "This doesn't work on scene root!"
-msgstr ""
+msgstr "Asta nu funcționează în rădăcina scenei!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Shape"
-msgstr ""
+msgstr "Creează o Formă Trimesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Shape"
-msgstr ""
+msgstr "Creează o Formă Convexă"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Navigation Mesh"
-msgstr ""
+msgstr "Creează un Mesh de Navigare"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Contained Mesh is not of type ArrayMesh."
-msgstr ""
+msgstr "Mesh-ul conținut nu este de tipul ArrayMesh."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "UV Unwrap failed, mesh may not be manifold?"
-msgstr ""
+msgstr "Despachetarea UV a eșuat, se poate ca mesh-ul să nu fie multiplu?"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "No mesh to debug."
-msgstr ""
+msgstr "Niciun mesh de depanat."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Model has no UV in this layer"
-msgstr ""
+msgstr "Modelul nu are UV în acest strat"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "MeshInstance lacks a Mesh!"
-msgstr ""
+msgstr "MeshInstance nu are un Mesh!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh has not surface to create outlines from!"
-msgstr ""
+msgstr "Mesh-ul nu are o suprafață din care să se poată creea contururi!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
-msgstr ""
+msgstr "Mesh-ul primitiv nu este de tipul PRIMITIVE_TRIANGLES!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
-msgstr ""
+msgstr "Nu s-a putut creea un contur!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline"
-msgstr ""
+msgstr "Creează Contur"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Mesh"
-msgstr ""
+msgstr "Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Static Body"
-msgstr ""
+msgstr "Creează un Corp Static Trimesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Static Body"
-msgstr ""
+msgstr "Creează un Corp Static Convex"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Collision Sibling"
-msgstr ""
+msgstr "Creează un Frate de Coliziune Trimesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Convex Collision Sibling"
-msgstr ""
+msgstr "Creează un Frate de Coliziune Convex"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline Mesh..."
-msgstr ""
+msgstr "Se Creează un Mesh de Contur..."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV1"
-msgstr ""
+msgstr "Vizionare UV1"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "View UV2"
-msgstr ""
+msgstr "Vizionare UV2"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Unwrap UV2 for Lightmap/AO"
-msgstr ""
+msgstr "Despachetează UV2 pentru Lightmap/AO"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline Mesh"
-msgstr ""
+msgstr "Creează Mesh de Contur"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Outline Size:"
-msgstr ""
+msgstr "Dimensiunea Conturului:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No mesh source specified (and no MultiMesh set in node)."
-msgstr ""
+msgstr "Niciun mesh sursă specificată (și niciun MultiMesh setat în nod)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No mesh source specified (and MultiMesh contains no Mesh)."
-msgstr ""
+msgstr "Niciun mesh sursă specificată (și MultiMesh nu conține un Mesh)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (invalid path)."
-msgstr ""
+msgstr "Sursa mesh-ului nevalidă (cale nevalidă)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (not a MeshInstance)."
-msgstr ""
+msgstr "Sursa mesh-ului nevalidă (nu este un MeshInstance)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh source is invalid (contains no Mesh resource)."
-msgstr ""
+msgstr "Sursa mesh-ului nevalidă (nu conține nicio resursă Mesh)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No surface source specified."
-msgstr ""
+msgstr "Nicio sursă de suprafață specificată."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (invalid path)."
-msgstr ""
+msgstr "Sursa suprafeței nevalidă (cale nevalidă)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (no geometry)."
-msgstr ""
+msgstr "Sursa suprafeței nevalidă (nu există geometrie)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Surface source is invalid (no faces)."
-msgstr ""
+msgstr "Sursa suprafeței nevalidă (nu există fețe)."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Parent has no solid faces to populate."
-msgstr ""
+msgstr "Părintele nu are fețe solide pentru a fi populate."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Couldn't map area."
-msgstr ""
+msgstr "Nu s-a putut mapa zona."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Select a Source Mesh:"
-msgstr ""
+msgstr "Selectează un Mesh Sursă:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Select a Target Surface:"
-msgstr ""
+msgstr "Selectează o Suprafață Țintă:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate Surface"
-msgstr ""
+msgstr "Populează Suprafața"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate MultiMesh"
-msgstr ""
+msgstr "Populează MultiMesh"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Target Surface:"
-msgstr ""
+msgstr "Suprafață Țintă:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Source Mesh:"
-msgstr ""
+msgstr "Mesh Sursă:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "X-Axis"
-msgstr ""
+msgstr "Axa-X"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Y-Axis"
-msgstr ""
+msgstr "Axa-Y"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Z-Axis"
-msgstr ""
+msgstr "Axa-Z"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Mesh Up Axis:"
@@ -4091,7 +4157,7 @@ msgstr ""
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Random Scale:"
-msgstr ""
+msgstr "Dimensiune Aleatorie:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate"
@@ -4103,11 +4169,11 @@ msgstr ""
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Bake the navigation mesh."
-msgstr ""
+msgstr "Procesează mesh-ul de navigare."
#: editor/plugins/navigation_mesh_editor_plugin.cpp
msgid "Clear the navigation mesh."
-msgstr ""
+msgstr "Curăță mesh-ul de navigare."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Setting up Configuration..."
@@ -4147,11 +4213,11 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Converting to native navigation mesh..."
-msgstr ""
+msgstr "Se convertește în mesh nativ de navigare..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Navigation Mesh Generator Setup:"
-msgstr ""
+msgstr "Setup Generare Mesh de Navigare:"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Parsing Geometry..."
@@ -4192,7 +4258,7 @@ msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Clear Emission Mask"
-msgstr ""
+msgstr "Curăță Masca de Emisie"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
@@ -4246,7 +4312,7 @@ msgstr ""
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Mesh"
-msgstr ""
+msgstr "Creează Puncte de Emisie Din Mesh"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
@@ -4411,7 +4477,7 @@ msgstr ""
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift+Ctrl: Scale"
-msgstr ""
+msgstr "Shift+Ctrl: Dimensiune"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Move Polygon"
@@ -4423,7 +4489,7 @@ msgstr ""
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Scale Polygon"
-msgstr ""
+msgstr "Redimensionează Poligon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4443,16 +4509,16 @@ msgstr ""
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Clear UV"
-msgstr ""
+msgstr "Curăță UV"
#: editor/plugins/polygon_2d_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap"
-msgstr ""
+msgstr "Snap"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Enable Snap"
-msgstr ""
+msgstr "Activează Snap"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid"
@@ -4513,7 +4579,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
msgid "Clear Recent Files"
-msgstr ""
+msgstr "Curăță Fișierele Recente"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close and save changes?"
@@ -4629,7 +4695,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
msgid "Run"
-msgstr ""
+msgstr "Execută"
#: editor/plugins/script_editor_plugin.cpp
msgid "Toggle Scripts Panel"
@@ -5219,7 +5285,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale Mode (R)"
-msgstr ""
+msgstr "Mod Redimensionare (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
@@ -5231,7 +5297,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Mode (%s)"
-msgstr ""
+msgstr "Mod Snap (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
@@ -5291,7 +5357,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Tool Scale"
-msgstr ""
+msgstr "Unealtă Dimensiune"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Toggle Freelook"
@@ -5348,19 +5414,19 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Settings"
-msgstr ""
+msgstr "Setări Snap"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate Snap:"
-msgstr ""
+msgstr "Tradu Snap:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate Snap (deg.):"
-msgstr ""
+msgstr "Rotație Snap (grade):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale Snap (%):"
-msgstr ""
+msgstr "Dimensionare Snap (%):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Viewport Settings"
@@ -5392,7 +5458,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale (ratio):"
-msgstr ""
+msgstr "Dimensiune (raport):"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Type"
@@ -5488,7 +5554,7 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Snap Mode:"
-msgstr ""
+msgstr "Mod Snap:"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "<None>"
@@ -5496,11 +5562,11 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Pixel Snap"
-msgstr ""
+msgstr "Pixel Snap"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Grid Snap"
-msgstr ""
+msgstr "Snap Grilă"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Auto Slice"
@@ -5901,9 +5967,8 @@ msgid "Imported Project"
msgstr ""
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Nume nevalid."
+msgstr "Nume de Proiect Nevalid."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6005,16 +6070,21 @@ msgid ""
"Please edit the project and set the main scene in \"Project Settings\" under "
"the \"Application\" category."
msgstr ""
+"Proiectul nu poate fi executat: nicio scenă principală nu a fost definită.\n"
+"Te rog editează proiectul și setează o scenă principală în \"Setările "
+"Proiectului\" din categoria \"Aplicații\"."
#: editor/project_manager.cpp
msgid ""
"Can't run project: Assets need to be imported.\n"
"Please edit the project to trigger the initial import."
msgstr ""
+"Nu se poate executa priectul: există Asset-uri care trebuie importate.\n"
+"Te rog editează proiectul pentru a declanșa importul inițial."
#: editor/project_manager.cpp
msgid "Are you sure to run more than one project?"
-msgstr ""
+msgstr "Ești sigur că vrei să execuți acel proiect?"
#: editor/project_manager.cpp
msgid "Remove project from the list? (Folder contents will not be modified)"
@@ -6066,13 +6136,16 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Can't run project"
-msgstr ""
+msgstr "Proiectul nu poate fi executat"
#: editor/project_manager.cpp
msgid ""
"You don't currently have any projects.\n"
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
+"Deocamdată nu ai niciun proiect.\n"
+"Dorești să explorezi exemplele de proiecte oficiale din Librăria de Asset-"
+"uri?"
#: editor/project_settings_editor.cpp
msgid "Key "
@@ -6298,7 +6371,7 @@ msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr ""
+msgstr "General"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
@@ -6506,7 +6579,7 @@ msgstr ""
#: editor/run_settings_dialog.cpp
msgid "Run Mode:"
-msgstr ""
+msgstr "Modul de Execuție:"
#: editor/run_settings_dialog.cpp
msgid "Current Scene"
@@ -6522,7 +6595,7 @@ msgstr ""
#: editor/run_settings_dialog.cpp
msgid "Scene Run Settings"
-msgstr ""
+msgstr "Setările de Execuție ale Scenei"
#: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp
#: scene/gui/dialogs.cpp
@@ -6627,7 +6700,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance"
-msgstr ""
+msgstr "Curăță Derivarea"
#: editor/scene_tree_dock.cpp
msgid "Delete Node(s)"
@@ -6651,7 +6724,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Clear Script"
-msgstr ""
+msgstr "Curăță Scriptul"
#: editor/scene_tree_dock.cpp
msgid "Merge From Scene"
@@ -6689,7 +6762,7 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
-msgstr ""
+msgstr "Curăță un script pentru nodul selectat."
#: editor/scene_tree_dock.cpp
msgid "Remote"
@@ -6701,11 +6774,11 @@ msgstr ""
#: editor/scene_tree_dock.cpp
msgid "Clear Inheritance? (No Undo!)"
-msgstr ""
+msgstr "Curăță Derivarea? (Fără Întoarcere)"
#: editor/scene_tree_dock.cpp
msgid "Clear!"
-msgstr ""
+msgstr "Curăță!"
#: editor/scene_tree_editor.cpp
msgid "Toggle Spatial Visible"
@@ -7197,7 +7270,7 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Snap View"
-msgstr ""
+msgstr "Perspectivă Snap"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clip Disabled"
@@ -7249,7 +7322,7 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Cursor Clear Rotation"
-msgstr ""
+msgstr "Curăță Rotația Cursorului"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Create Area"
@@ -7265,7 +7338,7 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Clear Selection"
-msgstr ""
+msgstr "Curăță Selecția"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "GridMap Settings"
@@ -7669,11 +7742,11 @@ msgstr ""
#: platform/javascript/export/export.cpp
msgid "Run in Browser"
-msgstr ""
+msgstr "Execută în Browser"
#: platform/javascript/export/export.cpp
msgid "Run exported HTML in the system's default browser."
-msgstr ""
+msgstr "Execută HTML-ul exportat în browserul prestabilit al sistemului."
#: platform/javascript/export/export.cpp
msgid "Could not write file:"
@@ -8008,3 +8081,11 @@ msgstr ""
#: scene/resources/dynamic_font.cpp
msgid "Invalid font size."
msgstr ""
+
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Fila anterioară"
+
+#, fuzzy
+#~ msgid "Next"
+#~ msgstr "Fila următoare"
diff --git a/editor/translations/ru.po b/editor/translations/ru.po
index 3a939ae94e..97c7284404 100644
--- a/editor/translations/ru.po
+++ b/editor/translations/ru.po
@@ -2,7 +2,7 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
+# Ðркадий ÐÐ²Ð°Ñ <savvot@gmail.com>, 2018.
# Artem Varaksa <aymfst@gmail.com>, 2018.
# B10nicMachine <shumik1337@gmail.com>, 2017.
# Chaosus89 <chaosus89@gmail.com>, 2018.
@@ -14,14 +14,14 @@
# Maxim toby3d Lebedev <mail@toby3d.ru>, 2016.
# outbools <drag4e@yandex.ru>, 2017.
# pitchblack <pitchblack@mail.ru>, 2017.
+# Sergey <maligin.serega2010@yandex.ru>, 2018.
# Sergey Agarkov <zorgsoft@gmail.com>, 2017.
-# Ðркадий ÐÐ²Ð°Ñ <savvot@gmail.com>, 2018.
-#
+# teriva <spirin.cos@yandex.ru>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-05-22 17:41+0000\n"
+"PO-Revision-Date: 2018-06-18 19:42+0000\n"
"Last-Translator: ijet <my-ijet@mail.ru>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/"
"godot/ru/>\n"
@@ -31,7 +31,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -2109,7 +2109,7 @@ msgstr "СиÑтема отÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ðº"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
-msgstr "ОбщеÑтвенные"
+msgstr "СообщеÑтво"
#: editor/editor_node.cpp
msgid "About"
@@ -3728,7 +3728,7 @@ msgstr "Показать окно проÑмотра"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
-msgstr "Центрировать на выбранном"
+msgstr "Центрировать выбранное"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Frame Selection"
@@ -5706,9 +5706,8 @@ msgid "Options"
msgstr "Параметры"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Имеет,Много,Разных,Опций!"
+msgstr "ЕÑть,Много,Вариантов"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5991,9 +5990,8 @@ msgid "Imported Project"
msgstr "Импортированный проект"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Ðазвание проекта:"
+msgstr "ÐедопуÑтимое Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6194,13 +6192,12 @@ msgid "Mouse Button"
msgstr "Кнопка мыши"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"ÐедопуÑтимое Ð¸Ð¼Ñ Ð´ÐµÐ¹ÑтвиÑ. Оно не может быть пуÑтым или Ñодержать '/', ':', "
-"'=', '\\' или '\"'"
+"'=', '\\' или '\"'."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8231,6 +8228,13 @@ msgstr "Ошибка загрузки шрифта."
msgid "Invalid font size."
msgstr "ÐедопуÑтимый размер шрифта."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
+
+#~ msgid "Next"
+#~ msgstr "Следующий"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "ÐедопуÑтимое название дейÑÑ‚Ð²Ð¸Ñ (подойдёт вÑÑ‘ кроме '/' или ':')."
@@ -8257,9 +8261,6 @@ msgstr "ÐедопуÑтимый размер шрифта."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "ОтÑутÑтвует project.godot в папке проекта."
-#~ msgid "Next"
-#~ msgstr "Следующий"
-
#~ msgid "Not found!"
#~ msgstr "Ðе найдено!"
diff --git a/editor/translations/sk.po b/editor/translations/sk.po
index 9a95848f70..9716dee696 100644
--- a/editor/translations/sk.po
+++ b/editor/translations/sk.po
@@ -2,25 +2,24 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# J08nY <johnenter@gmail.com>, 2016.
-#
+# MineGame 159 <minegame459@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2016-06-25 14:16+0000\n"
-"Last-Translator: J08nY <johnenter@gmail.com>\n"
+"PO-Revision-Date: 2018-06-18 08:43+0000\n"
+"Last-Translator: MineGame 159 <minegame459@gmail.com>\n"
"Language-Team: Slovak <https://hosted.weblate.org/projects/godot-engine/"
"godot/sk/>\n"
"Language: sk\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-"X-Generator: Weblate 2.7-dev\n"
+"X-Generator: Weblate 3.0.1\n"
#: editor/animation_editor.cpp
msgid "Disabled"
-msgstr ""
+msgstr "Vypnuté"
#: editor/animation_editor.cpp
msgid "All Selection"
@@ -28,11 +27,11 @@ msgstr "Všetky vybrané"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr ""
+msgstr "Animácia Zmeniť Keyframe Čas"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr ""
+msgstr "Animácia zmeniť prechod"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -40,11 +39,12 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr ""
+msgstr "Animácia Zmeniť Keyframe Hodnotu"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Change Call"
-msgstr ""
+msgstr "Animácia Zmeniť Hovor"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
@@ -68,7 +68,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr ""
+msgstr "Nastaviť prechody na:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
@@ -92,7 +92,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
-msgstr ""
+msgstr "Upraviť výber krivky"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
@@ -101,20 +101,19 @@ msgstr ""
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr ""
+msgstr "Duplikovať výber"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
msgstr ""
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Remove Selection"
-msgstr "Všetky vybrané"
+msgstr "Odstrániť výber"
#: editor/animation_editor.cpp
msgid "Continuous"
-msgstr ""
+msgstr "Priebežný"
#: editor/animation_editor.cpp
msgid "Discrete"
@@ -134,19 +133,19 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr ""
+msgstr "Zmeniť veľkosť výberu"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
-msgstr ""
+msgstr "Zmeniť veľkosť od kurzora"
#: editor/animation_editor.cpp
msgid "Goto Next Step"
-msgstr ""
+msgstr "PrejsÅ¥ na Äalší krok"
#: editor/animation_editor.cpp
msgid "Goto Prev Step"
-msgstr ""
+msgstr "Prejsť na predchádzajúci krok"
#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
#: editor/property_editor.cpp
@@ -159,23 +158,25 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "In"
-msgstr ""
+msgstr "V"
#: editor/animation_editor.cpp
msgid "Out"
-msgstr ""
+msgstr "Von"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "In-Out"
-msgstr ""
+msgstr "V-Von"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Out-In"
-msgstr ""
+msgstr "Von-V"
#: editor/animation_editor.cpp
msgid "Transitions"
-msgstr ""
+msgstr "Prechody"
#: editor/animation_editor.cpp
msgid "Optimize Animation"
@@ -8050,7 +8051,7 @@ msgstr ""
#: scene/resources/dynamic_font.cpp
msgid "Invalid font size."
-msgstr ""
+msgstr "Nesprávna veľkosť písma."
#, fuzzy
#~ msgid "Can't write file."
diff --git a/editor/translations/sl.po b/editor/translations/sl.po
index a762d6f69b..0fe619654f 100644
--- a/editor/translations/sl.po
+++ b/editor/translations/sl.po
@@ -2,17 +2,15 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# matevž lapajne <sivar.lapajne@gmail.com>, 2016-2018.
# Matjaž Vitas <matjaz.vitas@gmail.com>, 2017-2018.
# Miha Komatar <miha.komatar@gmail.com>, 2018.
# Simon Å ander <simon.sand3r@gmail.com>, 2017.
# Yahara Octanis <yaharao55@gmail.com>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-06-05 19:27+0000\n"
+"PO-Revision-Date: 2018-06-10 08:44+0000\n"
"Last-Translator: matevž lapajne <sivar.lapajne@gmail.com>\n"
"Language-Team: Slovenian <https://hosted.weblate.org/projects/godot-engine/"
"godot/sl/>\n"
@@ -21,7 +19,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3;\n"
-"X-Generator: Weblate 3.0\n"
+"X-Generator: Weblate 3.0.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -2860,36 +2858,36 @@ msgstr "Skupine"
#: editor/node_dock.cpp
msgid "Select a Node to edit Signals and Groups."
-msgstr ""
+msgstr "Za urejanje Signalov in Skupin izberi Gradnik."
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Poly"
-msgstr ""
+msgstr "Ustvarite Poligon"
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/collision_polygon_editor_plugin.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit Poly"
-msgstr ""
+msgstr "Uredi Poligon"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Insert Point"
-msgstr ""
+msgstr "Ustavi ToÄko"
#: editor/plugins/abstract_polygon_2d_editor.cpp
#: editor/plugins/collision_polygon_editor_plugin.cpp
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Edit Poly (Remove Point)"
-msgstr ""
+msgstr "Uredi Poligon (Odstrani ToÄko)"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Remove Poly And Point"
-msgstr ""
+msgstr "Odstrani Poligon in ToÄko"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Create a new polygon from scratch"
-msgstr ""
+msgstr "Ustvari nov poligon od zaÄetka"
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid ""
@@ -2898,6 +2896,10 @@ msgid ""
"Ctrl+LMB: Split Segment.\n"
"RMB: Erase Point."
msgstr ""
+"Uredi obstojeÄi poligon:\n"
+"LMG: Premakni ToÄko.\n"
+"Ctrl+LMG: Razdeli Älen.\n"
+"DMG: ZbriÅ¡i ToÄko."
#: editor/plugins/abstract_polygon_2d_editor.cpp
msgid "Delete points"
@@ -2905,154 +2907,154 @@ msgstr "IzbriÅ¡i toÄke"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Toggle Autoplay"
-msgstr ""
+msgstr "Preklop funkcije Samodejno Predvajanje"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Animation Name:"
-msgstr ""
+msgstr "Novo Ime Animacije:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "New Anim"
-msgstr ""
+msgstr "Nova Animacija"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Animation Name:"
-msgstr ""
+msgstr "Spremeni Ime Animacije:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Delete Animation?"
-msgstr ""
+msgstr "Izbrišem animacijo?"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Remove Animation"
-msgstr ""
+msgstr "Odstrani Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Invalid animation name!"
-msgstr ""
+msgstr "Napaka: Neveljavno ime animacije!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: Animation name already exists!"
-msgstr ""
+msgstr "NAPAKA: Animacija s tem imenom že obstaja!"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Rename Animation"
-msgstr ""
+msgstr "Preimenuj Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Add Animation"
-msgstr ""
+msgstr "Dodaj Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Next Changed"
-msgstr ""
+msgstr "Naslednjo Mešanje se je Spremenilo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Change Blend Time"
-msgstr ""
+msgstr "Spremeni Mešalni Čas"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load Animation"
-msgstr ""
+msgstr "Naloži Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Duplicate Animation"
-msgstr ""
+msgstr "Podvoji Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to copy!"
-msgstr ""
+msgstr "NAPAKA: Ni animacije za kopiranje!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation resource on clipboard!"
-msgstr ""
+msgstr "NAPAKA: Ni animacije virov na odložiÅ¡Äu!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Pasted Animation"
-msgstr ""
+msgstr "Prilepljena Animacija"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Paste Animation"
-msgstr ""
+msgstr "Prilepi animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "ERROR: No animation to edit!"
-msgstr ""
+msgstr "NAPAKA: Ni animacije za urejanje!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from current pos. (A)"
-msgstr ""
+msgstr "Predvajaj izbrano animacijo nazaj od trenutnega položaja. (A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation backwards from end. (Shift+A)"
-msgstr ""
+msgstr "Predvajaj izbrano animacijo nazaj od konca. (Shift+A)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Stop animation playback. (S)"
-msgstr ""
+msgstr "Ustavi predvajanje animacije. (S)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from start. (Shift+D)"
-msgstr ""
+msgstr "Predvajaj izbrano animacijo od zaÄetka. (Shift+D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Play selected animation from current pos. (D)"
-msgstr ""
+msgstr "Predvajaj izbrano animacijo iz trenutne pozicije. (D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation position (in seconds)."
-msgstr ""
+msgstr "Mesto animacije (v sekundah)."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Scale animation playback globally for the node."
-msgstr ""
+msgstr "Spremeni velikost predvajanja za gradnike globalno."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create new animation in player."
-msgstr ""
+msgstr "Ustvari novo animacijo v predvajalniku."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load animation from disk."
-msgstr ""
+msgstr "Naloži animacijo z diska."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Load an animation from disk."
-msgstr ""
+msgstr "Naloži animacijo z diska."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Save the current animation"
-msgstr ""
+msgstr "Shrani trenutno animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Display list of animations in player."
-msgstr ""
+msgstr "Prikaži seznam animacij v predvajalniku."
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Autoplay on Load"
-msgstr ""
+msgstr "Samodejno predvajaj ob nalaganju"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
-msgstr ""
+msgstr "Uredi Äas meÅ¡anice cilja"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Tools"
-msgstr ""
+msgstr "Animacijska Orodja"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Copy Animation"
-msgstr ""
+msgstr "Kopiraj Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Onion Skinning"
-msgstr ""
+msgstr "Lupljenje ÄŒebule"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Enable Onion Skinning"
-msgstr ""
+msgstr "OmogoÄi Lupljenje ÄŒebule"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Directions"
@@ -3060,35 +3062,35 @@ msgstr "Smeri"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Past"
-msgstr ""
+msgstr "Preteklost"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Future"
-msgstr ""
+msgstr "Prihodnost"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Depth"
-msgstr ""
+msgstr "Globina"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "1 step"
-msgstr ""
+msgstr "1 korak"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "2 steps"
-msgstr ""
+msgstr "2 koraka"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "3 steps"
-msgstr ""
+msgstr "3 koraki"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Differences Only"
-msgstr ""
+msgstr "Samo Razlike"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Force White Modulate"
-msgstr ""
+msgstr "Sile Bele Modulacije"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Include Gizmos (3D)"
@@ -3096,30 +3098,30 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create New Animation"
-msgstr ""
+msgstr "Ustvari Novo Animacijo"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Name:"
-msgstr ""
+msgstr "Ime Animacije:"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Error!"
-msgstr ""
+msgstr "Napaka!"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Blend Times:"
-msgstr ""
+msgstr "Čas Mešanja:"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Next (Auto Queue):"
-msgstr ""
+msgstr "Naprej (Samodejna Razvrstitev):"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Cross-Animation Blend Times"
-msgstr ""
+msgstr "Navzkrižna Animacija Časa Mešanice"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/canvas_item_editor_plugin.cpp
@@ -3128,7 +3130,7 @@ msgstr "Animacija"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "New name:"
-msgstr ""
+msgstr "Novo ime:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Filters"
@@ -3137,68 +3139,68 @@ msgstr "Uredi Filtre"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Scale:"
-msgstr ""
+msgstr "Prilagodi Velikost:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Fade In (s):"
-msgstr ""
+msgstr "Postopno Prikazovanje (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Fade Out (s):"
-msgstr ""
+msgstr "Postopno Izginevanje (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend"
-msgstr ""
+msgstr "Zmešaj"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix"
-msgstr ""
+msgstr "Mešaj"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Auto Restart:"
-msgstr ""
+msgstr "Samodejni Ponovni Zagon:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Restart (s):"
-msgstr ""
+msgstr "Znova Zaženi (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Random Restart (s):"
-msgstr ""
+msgstr "NakljuÄno Zaženi (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Start!"
-msgstr ""
+msgstr "Zaženi!"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Amount:"
-msgstr ""
+msgstr "KoliÄina:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend:"
-msgstr ""
+msgstr "Zmešaj:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend 0:"
-msgstr ""
+msgstr "Zmešaj 0:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend 1:"
-msgstr ""
+msgstr "Zmešaj 1:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "X-Fade Time (s):"
-msgstr ""
+msgstr "ÄŒas X-Bledenja (s):"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Current:"
-msgstr ""
+msgstr "Trenutno:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Add Input"
-msgstr ""
+msgstr "Dodaj Vnos"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Clear Auto-Advance"
@@ -3206,67 +3208,67 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Set Auto-Advance"
-msgstr ""
+msgstr "Nastavi Samodejno-Napredovanje"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Delete Input"
-msgstr ""
+msgstr "Izbriši Vnos"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation tree is valid."
-msgstr ""
+msgstr "Drevo animacije je veljavno."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation tree is invalid."
-msgstr ""
+msgstr "Drevo animacije ni veljavno."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation Node"
-msgstr ""
+msgstr "Animacijski Gradnik"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "OneShot Node"
-msgstr ""
+msgstr "Gradnik EnPoizkus"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Mix Node"
-msgstr ""
+msgstr "Gradnik Mešanica"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend2 Node"
-msgstr ""
+msgstr "Gradnik Zmešaj2"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend3 Node"
-msgstr ""
+msgstr "Gradnik Zmešaj3"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Blend4 Node"
-msgstr ""
+msgstr "Gradnik Zmešaj4"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeScale Node"
-msgstr ""
+msgstr "Gradnik ÄŒasovnoMerilo"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeSeek Node"
-msgstr ""
+msgstr "Gradnik ÄŒasovniIskalnik"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Transition Node"
-msgstr ""
+msgstr "Gradnik Prehod"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Import Animations..."
-msgstr ""
+msgstr "Uvozi Animacije..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
-msgstr ""
+msgstr "Uredi Gradnike Filtri"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Filters..."
-msgstr ""
+msgstr "Filtri..."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
@@ -3274,67 +3276,67 @@ msgstr "AnimacijskoDrevo"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
-msgstr ""
+msgstr "Prosto"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Contents:"
-msgstr ""
+msgstr "Vsebina:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "View Files"
-msgstr ""
+msgstr "Ogled datotek"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't resolve hostname:"
-msgstr ""
+msgstr "Ne morem razrešiti imena gostitelja:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Connection error, please try again."
-msgstr ""
+msgstr "Napaka pri povezavi, poskusi znova."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Can't connect to host:"
-msgstr ""
+msgstr "NemogoÄe se je povezati z gostiteljem:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "No response from host:"
-msgstr ""
+msgstr "Gostitelj se ne odziva:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request failed, return code:"
-msgstr ""
+msgstr "Zahteva ni uspela, povratna koda:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request failed, too many redirects"
-msgstr ""
+msgstr "Zahteva ni uspela, preveÄ preusmeritev"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Bad download hash, assuming file has been tampered with."
-msgstr ""
+msgstr "Slab prenos hash kode, predvidevamo, da je bila datoteka spremenjena."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Expected:"
-msgstr ""
+msgstr "PriÄakovano:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Got:"
-msgstr ""
+msgstr "Dobil:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Failed sha256 hash check"
-msgstr ""
+msgstr "Neuspešno preverjanje preizkusa sha256"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Asset Download Error:"
-msgstr ""
+msgstr "Napaka pri prenosu sredstev:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Fetching:"
-msgstr ""
+msgstr "Pridobivanje:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Resolving..."
-msgstr ""
+msgstr "Razreševanje..."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Error making request"
@@ -8078,6 +8080,14 @@ msgstr "Napaka nalaganja pisave."
msgid "Invalid font size."
msgstr "Neveljavna velikost pisave."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Prejšnji zavihek"
+
+#, fuzzy
+#~ msgid "Next"
+#~ msgstr "Naslednji zavihek"
+
#~ msgid "Not found!"
#~ msgstr "Ni Zadetka!"
diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po
index fbfc998111..c838174131 100644
--- a/editor/translations/sr_Cyrl.po
+++ b/editor/translations/sr_Cyrl.po
@@ -8149,6 +8149,13 @@ msgstr ""
msgid "Invalid font size."
msgstr "Ðеважећа величина фонта."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Претходни таб"
+
+#~ msgid "Next"
+#~ msgstr "Следеће"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
@@ -8159,9 +8166,6 @@ msgstr "Ðеважећа величина фонта."
#~ msgid "Can't write file."
#~ msgstr "ÐеуÑпех при запиÑивању датотеке."
-#~ msgid "Next"
-#~ msgstr "Следеће"
-
#~ msgid "Not found!"
#~ msgstr "Ðије пронађено!"
diff --git a/editor/translations/sv.po b/editor/translations/sv.po
index 1f1b6f1397..9ec654128a 100644
--- a/editor/translations/sv.po
+++ b/editor/translations/sv.po
@@ -3668,8 +3668,9 @@ msgid "first"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
+#, fuzzy
msgid "prev"
-msgstr ""
+msgstr "förhandsgranska"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "next"
@@ -8650,6 +8651,10 @@ msgstr "Fel vid laddning av font."
msgid "Invalid font size."
msgstr "Ogiltig teckenstorlek."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Föregående flik"
+
#~ msgid "Next"
#~ msgstr "Nästa"
@@ -8685,10 +8690,6 @@ msgstr "Ogiltig teckenstorlek."
#~ msgid "That's a BINGO!"
#~ msgstr "Det är en BINGO!"
-#, fuzzy
-#~ msgid "preview"
-#~ msgstr "förhandsgranska"
-
#~ msgid "Move Add Key"
#~ msgstr "Flytta Lägg Till Nyckel"
diff --git a/editor/translations/th.po b/editor/translations/th.po
index f49b08d272..4db8459f1b 100644
--- a/editor/translations/th.po
+++ b/editor/translations/th.po
@@ -8077,6 +8077,13 @@ msgstr "ผิดพลาดขณะโหลดฟอนต์"
msgid "Invalid font size."
msgstr "ขนาดฟอนต์ผิดพลาด"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "à¹à¸—็บà¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²"
+
+#~ msgid "Next"
+#~ msgstr "ต่อไป"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "ใช้ชื่อนี้ไม่ได้ (มี '/' หรือ ':')"
@@ -8100,9 +8107,6 @@ msgstr "ขนาดฟอนต์ผิดพลาด"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "ไม่พบไฟล์ project.godot"
-#~ msgid "Next"
-#~ msgstr "ต่อไป"
-
#~ msgid "Not found!"
#~ msgstr "ไม่พบ!"
diff --git a/editor/translations/tr.po b/editor/translations/tr.po
index 5be2415c0e..292cec4063 100644
--- a/editor/translations/tr.po
+++ b/editor/translations/tr.po
@@ -2,8 +2,8 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Aprın Çor Tigin <kabusturk38@gmail.com>, 2016-2017.
+# Aykut YILDIRIM <aykutyildirim@windowslive.com>, 2018.
# Ceyhun Can Ulker <ceyhuncanu@gmail.com>, 2016.
# Enes Kaya Öcal <ekayaocal@hotmail.com>, 2016.
# Enescan Yerlikaya <enescanyerlikaya@gmail.com>, 2017.
@@ -17,19 +17,18 @@
# razah <icnikerazah@gmail.com>, 2017-2018.
# stnmycri <satenmeycri@gmail.com>, 2017-2018.
# Yavuz Günay <yavuzgunay@gmail.com>, 2017.
-#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-06-05 05:45+0000\n"
-"Last-Translator: Kaan Gül <qaantum@hotmail.com>\n"
+"PO-Revision-Date: 2018-06-10 09:46+0000\n"
+"Last-Translator: Aykut YILDIRIM <aykutyildirim@windowslive.com>\n"
"Language-Team: Turkish <https://hosted.weblate.org/projects/godot-engine/"
"godot/tr/>\n"
"Language: tr\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.0\n"
+"X-Generator: Weblate 3.0.1-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5984,9 +5983,8 @@ msgid "Imported Project"
msgstr "İçe Aktarılan Proje"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Proje Adı:"
+msgstr "Geçersiz Proje Adı."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6191,6 +6189,7 @@ msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
+"Geçersiz işlem adı. Boş olamaz ve '/', ':', '=', '\\' veya '\"' içeremez."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -7420,9 +7419,8 @@ msgid "Mono"
msgstr "Tekli"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "About C# support"
-msgstr "C# hakkında destek"
+msgstr "C# desteği hakkında"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Create C# solution"
@@ -7995,12 +7993,11 @@ msgstr "ARVROrigin bir ARVRCamera çocuk düğümü gerektirir"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "(Time Left: %d:%02d s)"
-msgstr "(Kalan Zaman:%d:%02d s)"
+msgstr "(Kalan Zaman:%d:%02d sn)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -8102,7 +8099,7 @@ msgstr ""
#: scene/3d/scenario_fx.cpp
msgid "WorldEnvironment needs an Environment resource."
-msgstr ""
+msgstr "WorldEnvironment bir Environment kaynağı gerektirir."
#: scene/3d/scenario_fx.cpp
msgid ""
@@ -8116,6 +8113,8 @@ msgid ""
"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
"this environment's Background Mode to Canvas (for 2D scenes)."
msgstr ""
+"Bu WorldEnvironment yoksayıldı. (3B sahneler için) Bir Kamera ekleyin veya "
+"(2B sahneler için) bu ortamın Arkaplan Kipini Canvas olarak ayarlayın."
#: scene/3d/sprite_3d.cpp
msgid ""
@@ -8213,6 +8212,13 @@ msgstr "Yazıtipi yükleme hatası."
msgid "Invalid font size."
msgstr "Geçersiz yazıtipi boyutu."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Önceki sekme"
+
+#~ msgid "Next"
+#~ msgstr "Sonraki"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Geçersiz işlem (her şey ancak şu '/' ya da şuna ':' gider)."
@@ -8238,9 +8244,6 @@ msgstr "Geçersiz yazıtipi boyutu."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Proje yolunda proje.godot alınamadı."
-#~ msgid "Next"
-#~ msgstr "Sonraki"
-
#~ msgid "Not found!"
#~ msgstr "Bulunamadı!"
diff --git a/editor/translations/uk.po b/editor/translations/uk.po
index 730d6a38ca..067c7be724 100644
--- a/editor/translations/uk.po
+++ b/editor/translations/uk.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# Aleksandr <XpycT.TOP@gmail.com>, 2017.
# Yuri Chornoivan <yurchor@ukr.net>, 2018.
# Ðндрій Бандура <andriykopanytsia@gmail.com>, 2018.
@@ -10,12 +9,11 @@
# МакÑим Якимчук <xpinovo@gmail.com>, 2018.
# ÐœÐ°Ñ€Ñ Ð¯Ð¼Ð±Ð°Ñ€ <mjambarmeta@gmail.com>, 2017-2018.
# ОлекÑандр Пилипчук <pilipchukap@rambler.ru>, 2018.
-#
msgid ""
msgstr ""
"Project-Id-Version: Ukrainian (Godot Engine)\n"
-"PO-Revision-Date: 2018-05-18 10:42+0000\n"
-"Last-Translator: МакÑим Якимчук <xpinovo@gmail.com>\n"
+"PO-Revision-Date: 2018-06-06 04:03+0000\n"
+"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/"
"godot/uk/>\n"
"Language: uk\n"
@@ -23,7 +21,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -5700,9 +5698,8 @@ msgid "Options"
msgstr "Параметри"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "Має,Багато,Декілька,Параметрів!"
+msgstr "Має,Багато,Параметрів"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5989,9 +5986,8 @@ msgid "Imported Project"
msgstr "Імпортований проект"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "Ðазва проекту:"
+msgstr "Ðекоректна назва проекту."
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6193,13 +6189,12 @@ msgid "Mouse Button"
msgstr "Кнопка миші"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
msgstr ""
"Ðекоректна назва дії. Ðазва не може бути порожньою Ñ– не може міÑтити "
-"Ñимволів «/», «:», «=», «\\» та «\"»"
+"Ñимволів «/», «:», «=», «\\» та «\"»."
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -8230,6 +8225,13 @@ msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ."
msgid "Invalid font size."
msgstr "Ðекоректний розмір шрифту."
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°"
+
+#~ msgid "Next"
+#~ msgstr "Далі"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Ðекоректна Ð´Ñ–Ñ (можна уÑе, окрім «/» або «:»)."
@@ -8256,9 +8258,6 @@ msgstr "Ðекоректний розмір шрифту."
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ project.godot у каталозі проекту."
-#~ msgid "Next"
-#~ msgstr "Далі"
-
#~ msgid "Not found!"
#~ msgstr "Ðе знайдено!"
diff --git a/editor/translations/vi.po b/editor/translations/vi.po
index 2ed1d729c7..6651bd170c 100644
--- a/editor/translations/vi.po
+++ b/editor/translations/vi.po
@@ -7967,3 +7967,7 @@ msgstr "Lỗi tải font."
#: scene/resources/dynamic_font.cpp
msgid "Invalid font size."
msgstr "Kích thước font không hợp lệ."
+
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "Thư mục trước"
diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po
index b04de228dd..48e30ceab3 100644
--- a/editor/translations/zh_CN.po
+++ b/editor/translations/zh_CN.po
@@ -2,7 +2,6 @@
# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
-#
# 360119124 <360119124@qq.com>, 2018.
# æŸ æª¬æ€æ‰‹ <lemonkiller@gmail.com>, 2018.
# 纯æ´çš„å蛋 <tqj.zyy@gmail.com>, 2016.
@@ -13,8 +12,9 @@
# dragonandy <dragonandy@foxmail.com>, 2017-2018.
# Geequlim <geequlim@gmail.com>, 2016-2018.
# jie Shi <meishijiemeimeimei@gmail.com>, 2018.
+# Jingtian Pan <panjingtian@126.com>, 2018.
# lalalaring <783482203@qq.com>, 2017.
-# Luo Jun <vipsbpig@gmail.com>, 2016-2017.
+# Luo Jun <vipsbpig@gmail.com>, 2016-2017, 2018.
# oberon-tonya <360119124@qq.com>, 2016.
# plumsky <x-wolf@163.com>, 2018.
# Qichunren <whyruby@gmail.com>, 2017.
@@ -25,13 +25,13 @@
# Youmu <konpaku.w@gmail.com>, 2017.
# yuetian <18829280955@163.com>, 2018.
# Zae Chao <zae.vito@live.com>, 2018.
-#
+# zwj36028 <23732399@qq.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: Chinese (Simplified) (Godot Engine)\n"
"POT-Creation-Date: 2018-01-20 12:15+0200\n"
-"PO-Revision-Date: 2018-05-11 03:34+0000\n"
-"Last-Translator: plumsky <x-wolf@163.com>\n"
+"PO-Revision-Date: 2018-06-09 03:55+0000\n"
+"Last-Translator: zwj36028 <23732399@qq.com>\n"
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
"godot-engine/godot/zh_Hans/>\n"
"Language: zh_CN\n"
@@ -39,7 +39,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 3.0-dev\n"
+"X-Generator: Weblate 3.0\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1403,7 +1403,7 @@ msgstr "清空输出"
#: editor/editor_node.cpp
msgid "Project export failed with error code %d."
-msgstr "é¡¹ç›®å¯¼å‡ºå¤±è´¥ï¼Œé”™è¯¯ä»£ç  "
+msgstr "é¡¹ç›®å¯¼å‡ºå¤±è´¥ï¼Œé”™è¯¯ä»£ç  %d。"
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
@@ -2748,11 +2748,11 @@ msgstr "导入独立的物体和动画"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials+Animations"
-msgstr "导入独立的æè´¨å’ŒåŠ¨ç”»"
+msgstr "与独立的æè´¨å’ŒåŠ¨ç”»ä¸€åŒå¯¼å…¥"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials+Animations"
-msgstr "å¯¼å…¥ç‹¬ç«‹çš„ç‰©ä½“ã€æè´¨å’ŒåŠ¨ç”»"
+msgstr "ä¸Žç‹¬ç«‹çš„ç‰©ä½“ã€æè´¨å’ŒåŠ¨ç”»ä¸€åŒå¯¼å…¥"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes"
@@ -3853,7 +3853,7 @@ msgstr "æŒ‰ä½ Shift å¯å•独编辑切线"
#: editor/plugins/gi_probe_editor_plugin.cpp
msgid "Bake GI Probe"
-msgstr "烘焙GI Probe"
+msgstr "渲染GI Probe"
#: editor/plugins/gradient_editor_plugin.cpp
msgid "Add/Remove Color Ramp Point"
@@ -5647,9 +5647,8 @@ msgid "Options"
msgstr "选项"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Has,Many,Options"
-msgstr "有,很多,几个,选项(Have,Many,Several,Options)ï¼"
+msgstr "有,很多,选项"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
@@ -5929,9 +5928,8 @@ msgid "Imported Project"
msgstr "已导入的项目"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Invalid Project Name."
-msgstr "项目åç§°:"
+msgstr "无效项目å称。"
#: editor/project_manager.cpp
msgid "Couldn't create folder."
@@ -6128,11 +6126,10 @@ msgid "Mouse Button"
msgstr "鼠标按键"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid ""
"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or "
"'\"'."
-msgstr "无效的æ“作å称。它ä¸èƒ½æ˜¯ç©ºçš„也ä¸èƒ½åŒ…å« '/', ':', '=', '\\' 或者 '\"'"
+msgstr "无效的æ“作å称。它ä¸èƒ½æ˜¯ç©ºçš„也ä¸èƒ½åŒ…å« '/', ':', '=', '\\' 或者 '\"'。"
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6388,7 +6385,7 @@ msgstr "地区é‡å®šå‘:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "地区"
+msgstr "区域"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
@@ -7349,7 +7346,7 @@ msgstr "完æˆ"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Failed to create C# project."
-msgstr "创建C#项目失败"
+msgstr "创建C#项目失败。"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Mono"
@@ -8100,6 +8097,13 @@ msgstr "加载字体出错。"
msgid "Invalid font size."
msgstr "字体大å°éžæ³•。"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "上一个目录"
+
+#~ msgid "Next"
+#~ msgstr "下一项"
+
#~ msgid "Invalid action (anything goes but '/' or ':')."
#~ msgstr "Actionåéžæ³•(ä¸å¾—包å«'/'或':')。"
@@ -8123,9 +8127,6 @@ msgstr "字体大å°éžæ³•。"
#~ msgid "Couldn't get project.godot in the project path."
#~ msgstr "无法在项目目录下找到project.godot文件。"
-#~ msgid "Next"
-#~ msgstr "下一项"
-
#~ msgid "Not found!"
#~ msgstr "未找到ï¼"
diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po
index 2a0941da8e..568390a7a8 100644
--- a/editor/translations/zh_HK.po
+++ b/editor/translations/zh_HK.po
@@ -8295,6 +8295,13 @@ msgid "Invalid font size."
msgstr "無效字型"
#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "上一個tab"
+
+#~ msgid "Next"
+#~ msgstr "下一個"
+
+#, fuzzy
#~ msgid "Can't contain '/' or ':'"
#~ msgstr "ä¸èƒ½é€£åˆ°ä¸»æ©Ÿï¼š"
@@ -8302,9 +8309,6 @@ msgstr "無效字型"
#~ msgid "Can't write file."
#~ msgstr "無法新增資料夾"
-#~ msgid "Next"
-#~ msgstr "下一個"
-
#~ msgid "Not found!"
#~ msgstr "找ä¸åˆ°!"
diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po
index c38ab9f25b..38b565a37f 100644
--- a/editor/translations/zh_TW.po
+++ b/editor/translations/zh_TW.po
@@ -3343,8 +3343,9 @@ msgid "first"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
+#, fuzzy
msgid "prev"
-msgstr ""
+msgstr "é è¦½:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "next"
@@ -8122,6 +8123,10 @@ msgstr "讀å–字體錯誤。"
msgid "Invalid font size."
msgstr "無效的字體大å°ã€‚"
+#, fuzzy
+#~ msgid "Previous"
+#~ msgstr "上個分é "
+
#~ msgid "Next"
#~ msgstr "下一個"
@@ -8140,10 +8145,6 @@ msgstr "無效的字體大å°ã€‚"
#~ msgid "Skip"
#~ msgstr "è·³éŽ"
-#, fuzzy
-#~ msgid "preview"
-#~ msgstr "é è¦½:"
-
#~ msgid "List:"
#~ msgstr "列表:"
diff --git a/icon.png b/icon.png
index 29c4a7b8fc..6ad9b43117 100644
--- a/icon.png
+++ b/icon.png
Binary files differ
diff --git a/logo.png b/logo.png
index 8cf3e15ebc..60e2ee9532 100644
--- a/logo.png
+++ b/logo.png
Binary files differ
diff --git a/main/SCsub b/main/SCsub
index e2bf03234f..0692175799 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -131,20 +131,20 @@ env.add_source_files(env.main_sources, "*.cpp")
controller_databases = ["#main/gamecontrollerdb.txt", "#main/gamecontrollerdb_205.txt", "#main/gamecontrollerdb_204.txt", "#main/godotcontrollerdb.txt"]
env.Depends("#main/default_controller_mappings.gen.cpp", controller_databases)
-env.Command("#main/default_controller_mappings.gen.cpp", controller_databases, make_default_controller_mappings)
+env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_databases, make_default_controller_mappings)
env.main_sources.append("#main/default_controller_mappings.gen.cpp")
Export('env')
env.Depends("#main/splash.gen.h", "#main/splash.png")
-env.Command("#main/splash.gen.h", "#main/splash.png", make_splash)
+env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", make_splash)
env.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
-env.Command("#main/splash_editor.gen.h", "#main/splash_editor.png", make_splash_editor)
+env.CommandNoCache("#main/splash_editor.gen.h", "#main/splash_editor.png", make_splash_editor)
env.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
-env.Command("#main/app_icon.gen.h", "#main/app_icon.png", make_app_icon)
+env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", make_app_icon)
SConscript('tests/SCsub')
diff --git a/main/app_icon.png b/main/app_icon.png
index 1d75cdc710..cf31af18a4 100644
--- a/main/app_icon.png
+++ b/main/app_icon.png
Binary files differ
diff --git a/main/main.cpp b/main/main.cpp
index 2e3c2f41b9..56dd5f73e7 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -715,6 +715,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
memdelete(sdr);
} else {
script_debugger = sdr;
+ sdr->set_allow_focus_steal_pid(allow_focus_steal_pid);
}
} else if (debug_mode == "local") {
@@ -824,7 +825,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->set_cmdline(execpath, main_args);
GLOBAL_DEF("rendering/quality/driver/driver_name", "GLES3");
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name", PropertyInfo(Variant::STRING, "rendering/quality/driver/driver_name", PROPERTY_HINT_ENUM, "GLES3,GLES2"));
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name", PropertyInfo(Variant::STRING, "rendering/quality/driver/driver_name", PROPERTY_HINT_ENUM, "GLES2,GLES3"));
if (video_driver == "") {
video_driver = GLOBAL_GET("rendering/quality/driver/driver_name");
}
@@ -867,6 +868,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/allow_per_pixel_transparency", false);
video_mode.use_vsync = GLOBAL_DEF("display/window/vsync/use_vsync", true);
+ OS::get_singleton()->_use_vsync = video_mode.use_vsync;
+
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency", false);
video_mode.layered_splash = GLOBAL_DEF("display/window/per_pixel_transparency_splash", false);
@@ -911,7 +914,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (audio_driver == "") { // specified in project.godot
- audio_driver = GLOBAL_DEF("audio/driver", OS::get_singleton()->get_audio_driver_name(0));
+ audio_driver = GLOBAL_DEF_RST("audio/driver", OS::get_singleton()->get_audio_driver_name(0));
}
for (int i = 0; i < OS::get_singleton()->get_audio_driver_count(); i++) {
@@ -1177,10 +1180,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
#endif
- if (allow_focus_steal_pid) {
- OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid);
- }
-
MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts");
register_platform_apis();
@@ -1824,6 +1823,8 @@ bool Main::iteration() {
ScriptServer::get_language(i)->frame();
}
+ AudioServer::get_singleton()->update();
+
if (script_debugger) {
if (script_debugger->is_profiling()) {
script_debugger->profiling_set_frame_times(USEC_TO_SEC(frame_time), USEC_TO_SEC(idle_process_ticks), USEC_TO_SEC(physics_process_ticks), frame_slice);
@@ -1956,6 +1957,15 @@ void Main::cleanup() {
if (engine)
memdelete(engine);
+ if (OS::get_singleton()->is_restart_on_exit_set()) {
+ //attempt to restart with arguments
+ String exec = OS::get_singleton()->get_executable_path();
+ List<String> args = OS::get_singleton()->get_restart_on_exit_arguments();
+ OS::ProcessID pid = 0;
+ OS::get_singleton()->execute(exec, args, false, &pid);
+ OS::get_singleton()->set_restart_on_exit(false, List<String>()); //clear list (uses memory)
+ }
+
unregister_core_driver_types();
unregister_core_types();
diff --git a/main/performance.cpp b/main/performance.cpp
index fc915e2e76..70e0a5f7aa 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -32,6 +32,7 @@
#include "message_queue.h"
#include "os/os.h"
#include "scene/main/scene_tree.h"
+#include "servers/audio_server.h"
#include "servers/physics_2d_server.h"
#include "servers/physics_server.h"
#include "servers/visual_server.h"
@@ -68,6 +69,7 @@ void Performance::_bind_methods() {
BIND_ENUM_CONSTANT(PHYSICS_3D_ACTIVE_OBJECTS);
BIND_ENUM_CONSTANT(PHYSICS_3D_COLLISION_PAIRS);
BIND_ENUM_CONSTANT(PHYSICS_3D_ISLAND_COUNT);
+ BIND_ENUM_CONSTANT(AUDIO_OUTPUT_LATENCY);
BIND_ENUM_CONSTANT(MONITOR_MAX);
}
@@ -104,6 +106,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
"physics_3d/active_objects",
"physics_3d/collision_pairs",
"physics_3d/islands",
+ "audio/output_latency",
};
@@ -147,6 +150,7 @@ float Performance::get_monitor(Monitor p_monitor) const {
case PHYSICS_3D_ACTIVE_OBJECTS: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ACTIVE_OBJECTS);
case PHYSICS_3D_COLLISION_PAIRS: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_COLLISION_PAIRS);
case PHYSICS_3D_ISLAND_COUNT: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ISLAND_COUNT);
+ case AUDIO_OUTPUT_LATENCY: return AudioServer::get_singleton()->get_output_latency();
default: {}
}
@@ -186,6 +190,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_TIME,
};
diff --git a/main/performance.h b/main/performance.h
index 464226b517..de00df5ff9 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -77,6 +77,7 @@ public:
PHYSICS_3D_COLLISION_PAIRS,
PHYSICS_3D_ISLAND_COUNT,
//physics
+ AUDIO_OUTPUT_LATENCY,
MONITOR_MAX
};
diff --git a/main/splash.png b/main/splash.png
index 34be46557f..32960db65f 100644
--- a/main/splash.png
+++ b/main/splash.png
Binary files differ
diff --git a/main/splash_editor.png b/main/splash_editor.png
index d8677f1749..f003995d6f 100644
--- a/main/splash_editor.png
+++ b/main/splash_editor.png
Binary files differ
diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp
index 5c7633a0cf..5f3a2c1dc8 100644
--- a/main/tests/test_gdscript.cpp
+++ b/main/tests/test_gdscript.cpp
@@ -193,14 +193,6 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) {
case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
txt = "~" + _parser_expr(c_node->arguments[0]);
} break;
- case GDScriptParser::OperatorNode::OP_PREINC: {
- } break;
- case GDScriptParser::OperatorNode::OP_PREDEC: {
- } break;
- case GDScriptParser::OperatorNode::OP_INC: {
- } break;
- case GDScriptParser::OperatorNode::OP_DEC: {
- } break;
case GDScriptParser::OperatorNode::OP_IN: {
txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]);
} break;
@@ -455,10 +447,9 @@ static void _parser_show_class(const GDScriptParser::ClassNode *p_class, int p_i
print_line("\n");
}
- for (int i = 0; i < p_class->constant_expressions.size(); i++) {
-
- const GDScriptParser::ClassNode::Constant &constant = p_class->constant_expressions[i];
- _print_indent(p_indent, "const " + String(constant.identifier) + "=" + _parser_expr(constant.expression));
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
+ const GDScriptParser::ClassNode::Constant &constant = E->get();
+ _print_indent(p_indent, "const " + String(E->key()) + "=" + _parser_expr(constant.expression));
}
for (int i = 0; i < p_class->variables.size(); i++) {
diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp
index 63032597ed..7103b436e1 100644
--- a/main/tests/test_shader_lang.cpp
+++ b/main/tests/test_shader_lang.cpp
@@ -321,8 +321,8 @@ MainLoop *test() {
dt["fragment"].built_ins["ALBEDO"] = SL::TYPE_VEC3;
dt["fragment"].can_discard = true;
- Set<String> rm;
- rm.insert("popo");
+ Vector<StringName> rm;
+ rm.push_back("popo");
Set<String> types;
types.insert("spatial");
diff --git a/methods.py b/methods.py
index 6dca826b2b..33d503a41a 100644
--- a/methods.py
+++ b/methods.py
@@ -937,6 +937,11 @@ def android_add_res_dir(self, subpath):
if (base_path not in self.android_res_dirs):
self.android_res_dirs.append(base_path)
+def android_add_asset_dir(self, subpath):
+ base_path = self.Dir(".").abspath + "/modules/" + self.current_module + "/" + subpath
+ if (base_path not in self.android_asset_dirs):
+ self.android_asset_dirs.append(base_path)
+
def android_add_aidl_dir(self, subpath):
base_path = self.Dir(".").abspath + "/modules/" + self.current_module + "/" + subpath
if (base_path not in self.android_aidl_dirs):
@@ -1329,3 +1334,8 @@ def add_program(env, name, sources, **args):
program = env.Program(name, sources, **args)
env.NoCache(program)
return program
+
+def CommandNoCache(env, target, sources, command, **args):
+ result = env.Command(target, sources, command, **args)
+ env.NoCache(result)
+ return result
diff --git a/misc/dist/appimage/AppRun b/misc/dist/appimage/AppRun
deleted file mode 100755
index db3398a92a..0000000000
--- a/misc/dist/appimage/AppRun
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-HERE="$(dirname "$(readlink -f "${0}")")"
-"${HERE}"/godot $@
diff --git a/misc/dist/appimage/godot.desktop b/misc/dist/appimage/godot.desktop
deleted file mode 100644
index 545c491256..0000000000
--- a/misc/dist/appimage/godot.desktop
+++ /dev/null
@@ -1,9 +0,0 @@
-[Desktop Entry]
-Name=Godot Engine
-GenericName=Libre game engine
-Comment=Multi-platform 2D and 3D game engine with a feature rich editor
-Exec=godot -pm
-Icon=godot
-Terminal=false
-Type=Application
-Categories=Development;IDE;
diff --git a/misc/dist/appimage/godot.png b/misc/dist/appimage/godot.png
deleted file mode 100644
index e334f5fa78..0000000000
--- a/misc/dist/appimage/godot.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
index 6b9b10daae..1299ceaee5 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
index 50497496b7..604a7ba701 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
index 1c489de69f..bffb8c9fde 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
index d82dfce936..47826cd683 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
index 5120595df8..0f44a704b5 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
index cf6cf1347a..07ab777bc2 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
index 4eb167ae18..774b9c5bbf 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
index a9f951deac..fff36679c7 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
index 06d16412e2..0804519faa 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
index d70cab17be..833142222c 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
index f934971074..4c934c4a53 100644
--- a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
index 70932c1943..6907ae4a9d 100644
--- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
+++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
@@ -41,11 +41,15 @@
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
$additional_plist_content
</dict>
diff --git a/misc/dist/uwp_template/Assets/SplashScreen.scale-100.png b/misc/dist/uwp_template/Assets/SplashScreen.scale-100.png
index 540bfb1c01..0c27fda8e7 100644
--- a/misc/dist/uwp_template/Assets/SplashScreen.scale-100.png
+++ b/misc/dist/uwp_template/Assets/SplashScreen.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/Square150x150Logo.scale-100.png b/misc/dist/uwp_template/Assets/Square150x150Logo.scale-100.png
index 6e307e5eb8..96871f7413 100644
--- a/misc/dist/uwp_template/Assets/Square150x150Logo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/Square150x150Logo.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/Square310x310Logo.scale-100.png b/misc/dist/uwp_template/Assets/Square310x310Logo.scale-100.png
index cb2516d7a0..96494b1020 100644
--- a/misc/dist/uwp_template/Assets/Square310x310Logo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/Square310x310Logo.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/Square44x44Logo.scale-100.png b/misc/dist/uwp_template/Assets/Square44x44Logo.scale-100.png
index 6e14223e87..d21bc42009 100644
--- a/misc/dist/uwp_template/Assets/Square44x44Logo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/Square44x44Logo.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/Square71x71Logo.scale-100.png b/misc/dist/uwp_template/Assets/Square71x71Logo.scale-100.png
index 0d4bd54da8..22a43cf95f 100644
--- a/misc/dist/uwp_template/Assets/Square71x71Logo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/Square71x71Logo.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/StoreLogo.scale-100.png b/misc/dist/uwp_template/Assets/StoreLogo.scale-100.png
index 1501a09557..3960b0424b 100644
--- a/misc/dist/uwp_template/Assets/StoreLogo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/StoreLogo.scale-100.png
Binary files differ
diff --git a/misc/dist/uwp_template/Assets/Wide310x150Logo.scale-100.png b/misc/dist/uwp_template/Assets/Wide310x150Logo.scale-100.png
index 593568e980..d836e113b1 100644
--- a/misc/dist/uwp_template/Assets/Wide310x150Logo.scale-100.png
+++ b/misc/dist/uwp_template/Assets/Wide310x150Logo.scale-100.png
Binary files differ
diff --git a/misc/travis/clang-format.sh b/misc/travis/clang-format.sh
index d1e37cc10e..5463998720 100755
--- a/misc/travis/clang-format.sh
+++ b/misc/travis/clang-format.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-CLANG_FORMAT=clang-format-5.0
+CLANG_FORMAT=clang-format-6.0
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
# Check the whole commit range against $TRAVIS_BRANCH, the base merge branch
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index 769119a0dc..919731b52b 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -53,7 +53,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
err = FAILED;
}
- if (bits_per_pixel != 24 || bits_per_pixel != 32) {
+ if (!(bits_per_pixel == 24 || bits_per_pixel == 32)) {
err = FAILED;
}
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 6246a295ec..2390c71b0a 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -113,6 +113,10 @@ RID BulletPhysicsServer::shape_create(ShapeType p_shape) {
shape = bulletnew(CapsuleShapeBullet);
} break;
+ case SHAPE_CYLINDER: {
+
+ shape = bulletnew(CylinderShapeBullet);
+ } break;
case SHAPE_CONVEX_POLYGON: {
shape = bulletnew(ConvexPolygonShapeBullet);
@@ -165,7 +169,7 @@ real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
}
RID BulletPhysicsServer::space_create() {
- SpaceBullet *space = bulletnew(SpaceBullet(false));
+ SpaceBullet *space = bulletnew(SpaceBullet);
CreateThenReturnRID(space_owner, space);
}
@@ -563,9 +567,6 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) {
void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) {
CollisionObjectBullet *body = get_collisin_object(p_body);
- if (!body) {
- body = soft_body_owner.get(p_body);
- }
ERR_FAIL_COND(!body);
body->set_instance_id(p_ID);
@@ -643,6 +644,20 @@ float BulletPhysicsServer::body_get_param(RID p_body, BodyParameter p_param) con
return body->get_param(p_param);
}
+void BulletPhysicsServer::body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_combine_mode(p_param, p_mode);
+}
+
+PhysicsServer::CombineMode BulletPhysicsServer::body_get_combine_mode(RID p_body, BodyParameter p_param) const {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, COMBINE_MODE_INHERIT);
+
+ return body->get_combine_mode(p_param);
+}
+
void BulletPhysicsServer::body_set_kinematic_safe_margin(RID p_body, real_t p_margin) {
RigidBodyBullet *body = rigid_body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -849,6 +864,13 @@ RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
CreateThenReturnRID(soft_body_owner, body);
}
+void BulletPhysicsServer::soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->update_visual_server(p_visual_server_handler);
+}
+
void BulletPhysicsServer::soft_body_set_space(RID p_body, RID p_space) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -875,11 +897,11 @@ RID BulletPhysicsServer::soft_body_get_space(RID p_body) const {
return space->get_self();
}
-void BulletPhysicsServer::soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void BulletPhysicsServer::soft_body_set_mesh(RID p_body, const REF &p_mesh) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_trimesh_body_shape(p_indices, p_vertices, p_triangles_num);
+ body->set_soft_mesh(p_mesh);
}
void BulletPhysicsServer::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
@@ -957,14 +979,16 @@ void BulletPhysicsServer::soft_body_set_transform(RID p_body, const Transform &p
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_transform(p_transform);
+ body->set_soft_transform(p_transform);
}
-Transform BulletPhysicsServer::soft_body_get_transform(RID p_body) const {
+Vector3 BulletPhysicsServer::soft_body_get_vertex_position(RID p_body, int vertex_index) const {
const SoftBodyBullet *body = soft_body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, Transform());
+ Vector3 pos;
+ ERR_FAIL_COND_V(!body, pos);
- return body->get_transform();
+ body->get_node_position(vertex_index, pos);
+ return pos;
}
void BulletPhysicsServer::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
@@ -979,6 +1003,154 @@ bool BulletPhysicsServer::soft_body_is_ray_pickable(RID p_body) const {
return body->is_ray_pickable();
}
+void BulletPhysicsServer::soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_simulation_precision(p_simulation_precision);
+}
+
+int BulletPhysicsServer::soft_body_get_simulation_precision(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_simulation_precision();
+}
+
+void BulletPhysicsServer::soft_body_set_total_mass(RID p_body, real_t p_total_mass) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_total_mass(p_total_mass);
+}
+
+real_t BulletPhysicsServer::soft_body_get_total_mass(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_total_mass();
+}
+
+void BulletPhysicsServer::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_linear_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_linear_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_linear_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_areaAngular_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_areaAngular_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_areaAngular_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_volume_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_volume_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_volume_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_pressure_coefficient(p_pressure_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pressure_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pressure_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ return body->set_pose_matching_coefficient(p_pose_matching_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pose_matching_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pose_matching_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_damping_coefficient(p_damping_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_damping_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_damping_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_drag_coefficient(p_drag_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_drag_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_drag_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_position(p_point_index, p_global_position);
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_global_position(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3(0., 0., 0.));
+ Vector3 pos;
+ body->get_node_position(p_point_index, pos);
+ return pos;
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_offset(RID p_body, int p_point_index) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3());
+ Vector3 res;
+ body->get_node_offset(p_point_index, res);
+ return res;
+}
+
+void BulletPhysicsServer::soft_body_remove_all_pinned_points(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->reset_all_node_mass();
+}
+
+void BulletPhysicsServer::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_mass(p_point_index, p_pin ? 0 : 1);
+}
+
+bool BulletPhysicsServer::soft_body_is_point_pinned(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_node_mass(p_point_index);
+}
+
PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const {
JointBullet *joint = joint_owner.get(p_joint);
ERR_FAIL_COND_V(!joint, JOINT_PIN);
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index e931915bba..2165845529 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -213,6 +213,9 @@ public:
virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value);
virtual float body_get_param(RID p_body, BodyParameter p_param) const;
+ virtual void body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode);
+ virtual CombineMode body_get_combine_mode(RID p_body, BodyParameter p_param) const;
+
virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin);
virtual real_t body_get_kinematic_safe_margin(RID p_body) const;
@@ -259,10 +262,12 @@ public:
virtual RID soft_body_create(bool p_init_sleeping = false);
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler);
+
virtual void soft_body_set_space(RID p_body, RID p_space);
virtual RID soft_body_get_space(RID p_body) const;
- virtual void soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh);
virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer);
virtual uint32_t soft_body_get_collision_layer(RID p_body) const;
@@ -277,12 +282,49 @@ public:
virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const;
+ /// Special function. This function has bad performance
virtual void soft_body_set_transform(RID p_body, const Transform &p_transform);
- virtual Transform soft_body_get_transform(RID p_body) const;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const;
virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable);
virtual bool soft_body_is_ray_pickable(RID p_body) const;
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision);
+ virtual int soft_body_get_simulation_precision(RID p_body);
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass);
+ virtual real_t soft_body_get_total_mass(RID p_body);
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_linear_stiffness(RID p_body);
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body);
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_volume_stiffness(RID p_body);
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient);
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body);
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient);
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body);
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient);
+ virtual real_t soft_body_get_damping_coefficient(RID p_body);
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient);
+ virtual real_t soft_body_get_drag_coefficient(RID p_body);
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position);
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index);
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body);
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin);
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index);
+
/* JOINT API */
virtual JointType joint_get_type(RID p_joint) const;
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 57e4db708e..1d63318fd7 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -111,6 +111,8 @@ void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_coll
void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
exceptions.insert(p_ignoreCollisionObject->get_self());
+ if (!bt_collision_object)
+ return;
bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, true);
if (space)
space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher());
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
index b119b7720f..a76b0438b4 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -32,19 +32,26 @@
#include "bullet_physics_server.h"
#include "class_db.h"
+#include "project_settings.h"
/**
@author AndreaCatania
*/
+#ifndef _3D_DISABLED
PhysicsServer *_createBulletPhysicsCallback() {
return memnew(BulletPhysicsServer);
}
+#endif
void register_bullet_types() {
-
+#ifndef _3D_DISABLED
PhysicsServerManager::register_server("Bullet", &_createBulletPhysicsCallback);
PhysicsServerManager::set_default_server("Bullet", 1);
+
+ GLOBAL_DEF("physics/3d/active_soft_world", true);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/active_soft_world", PropertyInfo(Variant::BOOL, "physics/3d/active_soft_world"));
+#endif
}
void unregister_bullet_types() {
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2494063c22..19fad283af 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -226,6 +226,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
case PhysicsServer::SHAPE_SPHERE:
case PhysicsServer::SHAPE_BOX:
case PhysicsServer::SHAPE_CAPSULE:
+ case PhysicsServer::SHAPE_CYLINDER:
case PhysicsServer::SHAPE_CONVEX_POLYGON:
case PhysicsServer::SHAPE_RAY: {
shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
@@ -256,6 +257,8 @@ RigidBodyBullet::RigidBodyBullet() :
angularDamp(0),
can_sleep(true),
omit_forces_integration(false),
+ restitution_combine_mode(PhysicsServer::COMBINE_MODE_INHERIT),
+ friction_combine_mode(PhysicsServer::COMBINE_MODE_INHERIT),
force_integration_callback(NULL),
isTransformChanged(false),
previousActiveState(true),
@@ -749,6 +752,22 @@ Vector3 RigidBodyBullet::get_angular_velocity() const {
return gVec;
}
+void RigidBodyBullet::set_combine_mode(const PhysicsServer::BodyParameter p_param, const PhysicsServer::CombineMode p_mode) {
+ if (p_param == PhysicsServer::BODY_PARAM_BOUNCE) {
+ restitution_combine_mode = p_mode;
+ } else {
+ friction_combine_mode = p_mode;
+ }
+}
+
+PhysicsServer::CombineMode RigidBodyBullet::get_combine_mode(PhysicsServer::BodyParameter p_param) const {
+ if (p_param == PhysicsServer::BODY_PARAM_BOUNCE) {
+ return restitution_combine_mode;
+ } else {
+ return friction_combine_mode;
+ }
+}
+
void RigidBodyBullet::set_transform__bullet(const btTransform &p_global_transform) {
if (mode == PhysicsServer::BODY_MODE_KINEMATIC) {
// The kinematic use MotionState class
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index b9511243c7..911e93bfef 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -200,6 +200,9 @@ private:
bool can_sleep;
bool omit_forces_integration;
+ PhysicsServer::CombineMode restitution_combine_mode;
+ PhysicsServer::CombineMode friction_combine_mode;
+
Vector<CollisionData> collisions;
// these parameters are used to avoid vector resize
int maxCollisionsDetection;
@@ -295,6 +298,12 @@ public:
void set_angular_velocity(const Vector3 &p_velocity);
Vector3 get_angular_velocity() const;
+ void set_combine_mode(const PhysicsServer::BodyParameter p_param, const PhysicsServer::CombineMode p_mode);
+ PhysicsServer::CombineMode get_combine_mode(PhysicsServer::BodyParameter p_param) const;
+
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_restitution_combine_mode() const { return restitution_combine_mode; }
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_friction_combine_mode() const { return friction_combine_mode; }
+
virtual void set_transform__bullet(const btTransform &p_global_transform);
virtual const btTransform &get_transform__bullet() const;
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 76d9614465..92e7c2df98 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -113,6 +113,10 @@ btCapsuleShapeZ *ShapeBullet::create_shape_capsule(btScalar radius, btScalar hei
return bulletnew(btCapsuleShapeZ(radius, height));
}
+btCylinderShape *ShapeBullet::create_shape_cylinder(btScalar radius, btScalar height) {
+ return bulletnew(btCylinderShape(btVector3(radius, height / 2.0, radius)));
+}
+
btConvexPointCloudShape *ShapeBullet::create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling) {
return bulletnew(btConvexPointCloudShape(&p_vertices[0], p_vertices.size(), p_local_scaling));
}
@@ -254,6 +258,39 @@ btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implici
return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
}
+/* Cylinder */
+
+CylinderShapeBullet::CylinderShapeBullet() :
+ ShapeBullet() {}
+
+void CylinderShapeBullet::set_data(const Variant &p_data) {
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("radius"));
+ ERR_FAIL_COND(!d.has("height"));
+ setup(d["height"], d["radius"]);
+}
+
+Variant CylinderShapeBullet::get_data() const {
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ return d;
+}
+
+PhysicsServer::ShapeType CylinderShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CYLINDER;
+}
+
+void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) {
+ radius = p_radius;
+ height = p_height;
+ notifyShapeChanged();
+}
+
+btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+ return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
+}
+
/* Convex polygon */
ConvexPolygonShapeBullet::ConvexPolygonShapeBullet() :
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index abeea0f9ce..16a5ac1fc6 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -82,6 +82,7 @@ public:
static class btSphereShape *create_shape_sphere(btScalar radius);
static class btBoxShape *create_shape_box(const btVector3 &boxHalfExtents);
static class btCapsuleShapeZ *create_shape_capsule(btScalar radius, btScalar height);
+ static class btCylinderShape *create_shape_cylinder(btScalar radius, btScalar height);
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
@@ -158,6 +159,25 @@ private:
void setup(real_t p_height, real_t p_radius);
};
+class CylinderShapeBullet : public ShapeBullet {
+
+ real_t height;
+ real_t radius;
+
+public:
+ CylinderShapeBullet();
+
+ _FORCE_INLINE_ real_t get_height() { return height; }
+ _FORCE_INLINE_ real_t get_radius() { return radius; }
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+
+private:
+ void setup(real_t p_height, real_t p_radius);
+};
+
class ConvexPolygonShapeBullet : public ShapeBullet {
public:
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 5c20eb73f1..b3680d58db 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -32,42 +32,24 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "scene/3d/immediate_geometry.h"
+#include "scene/3d/soft_body.h"
#include "space_bullet.h"
-/**
- @author AndreaCatania
-*/
-
SoftBodyBullet::SoftBodyBullet() :
CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY),
- mass(1),
+ total_mass(1),
simulation_precision(5),
- stiffness(0.5f),
- pressure_coefficient(50),
- damping_coefficient(0.005),
- drag_coefficient(0.005),
+ linear_stiffness(0.5),
+ areaAngular_stiffness(0.5),
+ volume_stiffness(0.5),
+ pressure_coefficient(0.),
+ pose_matching_coefficient(0.),
+ damping_coefficient(0.01),
+ drag_coefficient(0.),
bt_soft_body(NULL),
- soft_shape_type(SOFT_SHAPETYPE_NONE),
- isScratched(false),
- soft_body_shape_data(NULL) {
-
- test_geometry = memnew(ImmediateGeometry);
-
- red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
- red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
- red_mat->set_line_width(20.0);
- red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- red_mat->set_albedo(Color(1, 0, 0, 1));
- test_geometry->set_material_override(red_mat);
-
- test_is_in_scene = false;
-}
+ isScratched(false) {}
SoftBodyBullet::~SoftBodyBullet() {
- bulletdelete(soft_body_shape_data);
}
void SoftBodyBullet::reload_body() {
@@ -80,8 +62,6 @@ void SoftBodyBullet::reload_body() {
void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
isScratched = false;
-
- // Remove this object from the physics world
space->remove_soft_body(this);
}
@@ -90,86 +70,181 @@ void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
space->add_soft_body(this);
}
-
- reload_soft_body();
}
-void SoftBodyBullet::dispatch_callbacks() {
- if (!bt_soft_body) {
+void SoftBodyBullet::dispatch_callbacks() {}
+
+void SoftBodyBullet::on_collision_filters_change() {}
+
+void SoftBodyBullet::on_collision_checker_start() {}
+
+void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_server_handler) {
+ if (!bt_soft_body)
return;
+
+ /// Update visual server vertices
+ const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes);
+ const int nodes_count = nodes.size();
+
+ Vector<int> *vs_indices;
+ const void *vertex_position;
+ const void *vertex_normal;
+
+ for (int vertex_index = 0; vertex_index < nodes_count; ++vertex_index) {
+ vertex_position = reinterpret_cast<const void *>(&nodes[vertex_index].m_x);
+ vertex_normal = reinterpret_cast<const void *>(&nodes[vertex_index].m_n);
+
+ vs_indices = &indices_table[vertex_index];
+
+ const int vs_indices_size(vs_indices->size());
+ for (int x = 0; x < vs_indices_size; ++x) {
+ p_visual_server_handler->set_vertex((*vs_indices)[x], vertex_position);
+ p_visual_server_handler->set_normal((*vs_indices)[x], vertex_normal);
+ }
}
- if (!test_is_in_scene) {
- test_is_in_scene = true;
- SceneTree::get_singleton()->get_current_scene()->add_child(test_geometry);
+ /// Generate AABB
+ btVector3 aabb_min;
+ btVector3 aabb_max;
+ bt_soft_body->getAabb(aabb_min, aabb_max);
+
+ btVector3 size(aabb_max - aabb_min);
+
+ AABB aabb;
+ B_TO_G(aabb_min, aabb.position);
+ B_TO_G(size, aabb.size);
+
+ p_visual_server_handler->set_aabb(aabb);
+}
+
+void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) {
+
+ if (p_mesh.is_null() || !p_mesh->surface_is_softbody_friendly(0))
+ soft_mesh.unref();
+ else
+ soft_mesh = p_mesh;
+
+ if (soft_mesh.is_null()) {
+
+ destroy_soft_body();
+ return;
}
- test_geometry->clear();
- test_geometry->begin(Mesh::PRIMITIVE_LINES, NULL);
- bool first = true;
- Vector3 pos;
- for (int i = 0; i < bt_soft_body->m_nodes.size(); ++i) {
- const btSoftBody::Node &n = bt_soft_body->m_nodes[i];
- B_TO_G(n.m_x, pos);
- test_geometry->add_vertex(pos);
- if (!first) {
- test_geometry->add_vertex(pos);
- } else {
- first = false;
- }
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & VS::ARRAY_FORMAT_INDEX));
+ set_trimesh_body_shape(arrays[VS::ARRAY_INDEX], arrays[VS::ARRAY_VERTEX]);
+}
+
+void SoftBodyBullet::destroy_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+
+ if (space) {
+ /// Remove from world before deletion
+ space->remove_soft_body(this);
}
- test_geometry->end();
+
+ destroyBulletCollisionObject();
+ bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::set_soft_transform(const Transform &p_transform) {
+ reset_all_node_positions();
+ move_all_nodes(p_transform);
}
-void SoftBodyBullet::on_collision_filters_change() {
+void SoftBodyBullet::move_all_nodes(const Transform &p_transform) {
+ if (!bt_soft_body)
+ return;
+ btTransform bt_transf;
+ G_TO_B(p_transform, bt_transf);
+ bt_soft_body->transform(bt_transf);
}
-void SoftBodyBullet::on_collision_checker_start() {
+void SoftBodyBullet::set_node_position(int p_node_index, const Vector3 &p_global_position) {
+ btVector3 bt_pos;
+ G_TO_B(p_global_position, bt_pos);
+ set_node_position(p_node_index, bt_pos);
}
-void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {
+void SoftBodyBullet::set_node_position(int p_node_index, const btVector3 &p_global_position) {
+ if (bt_soft_body) {
+ bt_soft_body->m_nodes[p_node_index].m_x = p_global_position;
+ }
}
-void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {
+void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) const {
+ if (bt_soft_body) {
+ B_TO_G(bt_soft_body->m_nodes[p_node_index].m_x, r_position);
+ }
}
-void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void SoftBodyBullet::get_node_offset(int p_node_index, Vector3 &r_offset) const {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vertices(arrays[VS::ARRAY_VERTEX]);
- TrimeshSoftShapeData *shape_data = bulletnew(TrimeshSoftShapeData);
- shape_data->m_triangles_indices = p_indices;
- shape_data->m_vertices = p_vertices;
- shape_data->m_triangles_num = p_triangles_num;
+ if (0 <= p_node_index && vertices.size() > p_node_index) {
+ r_offset = vertices[p_node_index];
+ }
+}
- set_body_shape_data(shape_data, SOFT_SHAPE_TYPE_TRIMESH);
- reload_soft_body();
+void SoftBodyBullet::get_node_offset(int p_node_index, btVector3 &r_offset) const {
+ Vector3 off;
+ get_node_offset(p_node_index, off);
+ G_TO_B(off, r_offset);
}
-void SoftBodyBullet::set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type) {
- bulletdelete(soft_body_shape_data);
- soft_body_shape_data = p_soft_shape_data;
- soft_shape_type = p_type;
+void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) {
+ if (0 >= p_mass) {
+ pin_node(node_index);
+ } else {
+ unpin_node(node_index);
+ }
+ if (bt_soft_body) {
+ bt_soft_body->setMass(node_index, p_mass);
+ }
}
-void SoftBodyBullet::set_transform(const Transform &p_transform) {
- transform = p_transform;
+btScalar SoftBodyBullet::get_node_mass(int node_index) const {
if (bt_soft_body) {
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- //bt_soft_body->transform(bt_trans);
+ return bt_soft_body->getMass(node_index);
+ } else {
+ return -1 == search_node_pinned(node_index) ? 1 : 0;
}
}
-const Transform &SoftBodyBullet::get_transform() const {
- return transform;
+void SoftBodyBullet::reset_all_node_mass() {
+ if (bt_soft_body) {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 1);
+ }
+ }
+ pinned_nodes.resize(0);
}
-void SoftBodyBullet::get_first_node_origin(btVector3 &p_out_origin) const {
- if (bt_soft_body && bt_soft_body->m_nodes.size()) {
- p_out_origin = bt_soft_body->m_nodes[0].m_x;
- } else {
- p_out_origin.setZero();
+void SoftBodyBullet::reset_all_node_positions() {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vs_vertices(arrays[VS::ARRAY_VERTEX]);
+ PoolVector<Vector3>::Read vs_vertices_read = vs_vertices.read();
+
+ for (int vertex_index = bt_soft_body->m_nodes.size() - 1; 0 <= vertex_index; --vertex_index) {
+
+ G_TO_B(vs_vertices_read[indices_table[vertex_index][0]], bt_soft_body->m_nodes[vertex_index].m_x);
+
+ bt_soft_body->m_nodes[vertex_index].m_q = bt_soft_body->m_nodes[vertex_index].m_x;
+ bt_soft_body->m_nodes[vertex_index].m_v = btVector3(0, 0, 0);
+ bt_soft_body->m_nodes[vertex_index].m_f = btVector3(0, 0, 0);
}
}
@@ -181,22 +256,34 @@ void SoftBodyBullet::set_activation_state(bool p_active) {
}
}
-void SoftBodyBullet::set_mass(real_t p_val) {
+void SoftBodyBullet::set_total_mass(real_t p_val) {
if (0 >= p_val) {
p_val = 1;
}
- mass = p_val;
+ total_mass = p_val;
if (bt_soft_body) {
- bt_soft_body->setTotalMass(mass);
+ bt_soft_body->setTotalMass(total_mass);
}
}
-void SoftBodyBullet::set_stiffness(real_t p_val) {
- stiffness = p_val;
+void SoftBodyBullet::set_linear_stiffness(real_t p_val) {
+ linear_stiffness = p_val;
if (bt_soft_body) {
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ mat0->m_kLST = linear_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_areaAngular_stiffness(real_t p_val) {
+ areaAngular_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kAST = areaAngular_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_volume_stiffness(real_t p_val) {
+ volume_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kVST = volume_stiffness;
}
}
@@ -204,6 +291,9 @@ void SoftBodyBullet::set_simulation_precision(int p_val) {
simulation_precision = p_val;
if (bt_soft_body) {
bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
}
}
@@ -214,6 +304,13 @@ void SoftBodyBullet::set_pressure_coefficient(real_t p_val) {
}
}
+void SoftBodyBullet::set_pose_matching_coefficient(real_t p_val) {
+ pose_matching_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ }
+}
+
void SoftBodyBullet::set_damping_coefficient(real_t p_val) {
damping_coefficient = p_val;
if (bt_soft_body) {
@@ -228,89 +325,156 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) {
}
}
-void SoftBodyBullet::reload_soft_body() {
-
+void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices) {
+ /// Assert the current soft body is destroyed
destroy_soft_body();
- create_soft_body();
- if (bt_soft_body) {
+ /// Parse visual server indices to physical indices.
+ /// Merge all overlapping vertices and create a map of physical vertices to visual server
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- bt_soft_body->transform(bt_trans);
+ {
+ /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
+ Vector<int> vs_indices_to_physics_table;
- bt_soft_body->generateBendingConstraints(2, mat0);
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ { // Map vertices
+ indices_table.resize(0);
- bt_soft_body->m_cfg.piterations = simulation_precision;
- bt_soft_body->m_cfg.kDP = damping_coefficient;
- bt_soft_body->m_cfg.kDG = drag_coefficient;
- bt_soft_body->m_cfg.kPR = pressure_coefficient;
- bt_soft_body->setTotalMass(mass);
- }
- if (space) {
- // TODO remove this please
- space->add_soft_body(this);
- }
-}
+ int index = 0;
+ Map<Vector3, int> unique_vertices;
-void SoftBodyBullet::create_soft_body() {
- if (!space || !soft_body_shape_data) {
- return;
- }
- ERR_FAIL_COND(!space->is_using_soft_world());
- switch (soft_shape_type) {
- case SOFT_SHAPE_TYPE_TRIMESH: {
- TrimeshSoftShapeData *trimesh_data = static_cast<TrimeshSoftShapeData *>(soft_body_shape_data);
-
- Vector<int> indices;
- Vector<btScalar> vertices;
-
- int i;
- const int indices_size = trimesh_data->m_triangles_indices.size();
- const int vertices_size = trimesh_data->m_vertices.size();
- indices.resize(indices_size);
- vertices.resize(vertices_size * 3);
-
- PoolVector<int>::Read i_r = trimesh_data->m_triangles_indices.read();
- for (i = 0; i < indices_size; ++i) {
- indices[i] = i_r[i];
+ const int vs_vertices_size(p_vertices.size());
+
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int vs_vertex_index = 0; vs_vertex_index < vs_vertices_size; ++vs_vertex_index) {
+
+ Map<Vector3, int>::Element *e = unique_vertices.find(p_vertices_read[vs_vertex_index]);
+ int vertex_id;
+ if (e) {
+ // Already rxisting
+ vertex_id = e->value();
+ } else {
+ // Create new one
+ unique_vertices[p_vertices_read[vs_vertex_index]] = vertex_id = index++;
+ indices_table.push_back(Vector<int>());
+ }
+
+ indices_table[vertex_id].push_back(vs_vertex_index);
+ vs_indices_to_physics_table.push_back(vertex_id);
+ }
+ }
+
+ const int indices_map_size(indices_table.size());
+
+ Vector<btScalar> bt_vertices;
+
+ { // Parse vertices to bullet
+
+ bt_vertices.resize(indices_map_size * 3);
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int i = 0; i < indices_map_size; ++i) {
+ bt_vertices[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
+ bt_vertices[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
+ bt_vertices[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
}
- i_r = PoolVector<int>::Read();
+ }
+
+ Vector<int> bt_triangles;
+ const int triangles_size(p_indices.size() / 3);
+
+ { // Parse indices
+
+ bt_triangles.resize(triangles_size * 3);
+
+ PoolVector<int>::Read p_indices_read = p_indices.read();
- PoolVector<Vector3>::Read f_r = trimesh_data->m_vertices.read();
- for (int j = i = 0; i < vertices_size; ++i, j += 3) {
- vertices[j + 0] = f_r[i][0];
- vertices[j + 1] = f_r[i][1];
- vertices[j + 2] = f_r[i][2];
+ for (int i = 0; i < triangles_size; ++i) {
+ bt_triangles[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
+ bt_triangles[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
+ bt_triangles[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
}
- f_r = PoolVector<Vector3>::Read();
+ }
- bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(*space->get_soft_body_world_info(), vertices.ptr(), indices.ptr(), trimesh_data->m_triangles_num);
- } break;
- default:
- ERR_PRINT("Shape type not supported");
- return;
+ btSoftBodyWorldInfo fake_world_info;
+ bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false);
+ setup_soft_body();
}
+}
+
+void SoftBodyBullet::setup_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+ // Soft body setup
setupBulletCollisionObject(bt_soft_body);
- bt_soft_body->getCollisionShape()->setMargin(0.001f);
+ bt_soft_body->m_worldInfo = NULL; // Remove fake world info
+ bt_soft_body->getCollisionShape()->setMargin(0.01);
bt_soft_body->setCollisionFlags(bt_soft_body->getCollisionFlags() & (~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)));
+
+ // Space setup
+ if (space) {
+ space->add_soft_body(this);
+ }
+
mat0 = bt_soft_body->appendMaterial();
+
+ // Assign soft body data
+ bt_soft_body->generateBendingConstraints(2, mat0);
+
+ mat0->m_kLST = linear_stiffness;
+ mat0->m_kAST = areaAngular_stiffness;
+ mat0->m_kVST = volume_stiffness;
+
+ // Clusters allow to have Soft vs Soft collision but doesn't work well right now
+
+ //bt_soft_body->m_cfg.kSRHR_CL = 1;// Soft vs rigid hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSKHR_CL = 1;// Soft vs kinematic hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSSHR_CL = 1;// Soft vs soft hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSR_SPLT_CL = 1; // Soft vs rigid impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSK_SPLT_CL = 1; // Soft vs kinematic impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSS_SPLT_CL = 1; // Soft vs Soft impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.collisions = btSoftBody::fCollision::CL_SS + btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::VF_SS;
+ //bt_soft_body->generateClusters(64);
+
+ bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
+ bt_soft_body->m_cfg.kDP = damping_coefficient;
+ bt_soft_body->m_cfg.kDG = drag_coefficient;
+ bt_soft_body->m_cfg.kPR = pressure_coefficient;
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ bt_soft_body->setTotalMass(total_mass);
+
+ btSoftBodyHelpers::ReoptimizeLinkOrder(bt_soft_body);
+ bt_soft_body->updateBounds();
+
+ // Set pinned nodes
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 0);
+ }
}
-void SoftBodyBullet::destroy_soft_body() {
- if (space) {
- /// This step is required to assert that the body is not into the world during deletion
- /// This step is required since to change the body shape the body must be re-created.
- /// Here is handled the case when the body is assigned into a world and the body
- /// shape is changed.
- space->remove_soft_body(this);
+void SoftBodyBullet::pin_node(int p_node_index) {
+ if (-1 == search_node_pinned(p_node_index)) {
+ pinned_nodes.push_back(p_node_index);
}
- destroyBulletCollisionObject();
- bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::unpin_node(int p_node_index) {
+ const int id = search_node_pinned(p_node_index);
+ if (-1 != id) {
+ pinned_nodes.remove(id);
+ }
+}
+
+int SoftBodyBullet::search_node_pinned(int p_node_index) const {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ if (p_node_index == pinned_nodes[i]) {
+ return i;
+ }
+ }
+ return -1;
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index 9895643b84..c775193584 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -40,7 +40,10 @@
#define x11_None 0L
#endif
-#include <BulletSoftBody/btSoftBodyHelpers.h>
+#include "BulletSoftBody/btSoftBodyHelpers.h"
+#include "collision_object_bullet.h"
+#include "scene/resources/mesh.h"
+#include "servers/physics_server.h"
#ifdef x11_None
/// This is required to re add the macro None defined by x11 compiler
@@ -52,39 +55,34 @@
@author AndreaCatania
*/
-struct SoftShapeData {};
-struct TrimeshSoftShapeData : public SoftShapeData {
- PoolVector<int> m_triangles_indices;
- PoolVector<Vector3> m_vertices;
- int m_triangles_num;
-};
-
class SoftBodyBullet : public CollisionObjectBullet {
-public:
- enum SoftShapeType {
- SOFT_SHAPETYPE_NONE = 0,
- SOFT_SHAPE_TYPE_TRIMESH
- };
private:
btSoftBody *bt_soft_body;
+ Vector<Vector<int> > indices_table;
btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
- SoftShapeType soft_shape_type;
bool isScratched;
- SoftShapeData *soft_body_shape_data;
+ Ref<Mesh> soft_mesh;
- Transform transform;
int simulation_precision;
- real_t mass;
- real_t stiffness; // [0,1]
+ real_t total_mass;
+ real_t linear_stiffness; // [0,1]
+ real_t areaAngular_stiffness; // [0,1]
+ real_t volume_stiffness; // [0,1]
real_t pressure_coefficient; // [-inf,+inf]
+ real_t pose_matching_coefficient; // [0,1]
real_t damping_coefficient; // [0,1]
real_t drag_coefficient; // [0,1]
+ Vector<int> pinned_nodes;
- class ImmediateGeometry *test_geometry; // TODO remove this please
- Ref<SpatialMaterial> red_mat; // TODO remove this please
- bool test_is_in_scene; // TODO remove this please
+ // Other property to add
+ //btScalar kVC; // Volume conversation coefficient [0,+inf]
+ //btScalar kDF; // Dynamic friction coefficient [0,1]
+ //btScalar kMT; // Pose matching coefficient [0,1]
+ //btScalar kCHR; // Rigid contacts hardness [0,1]
+ //btScalar kKHR; // Kinetic contacts hardness [0,1]
+ //btScalar kSHR; // Soft contacts hardness [0,1]
public:
SoftBodyBullet();
@@ -101,39 +99,64 @@ public:
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
- void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
- void set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type);
+ void update_visual_server(class SoftBodyVisualServerHandler *p_visual_server_handler);
- void set_transform(const Transform &p_transform);
- /// This function doesn't return the exact COM transform.
- /// It returns the origin only of first node (vertice) of current soft body
- /// ---
- /// The soft body doesn't have a fixed center of mass, but is a group of nodes (vertices)
- /// that each has its own position in the world.
- /// For this reason return the correct COM is not so simple and must be calculate
- /// Check this to improve this function http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=8803
- const Transform &get_transform() const;
- void get_first_node_origin(btVector3 &p_out_origin) const;
+ void set_soft_mesh(const Ref<Mesh> &p_mesh);
+ void destroy_soft_body();
+
+ // Special function. This function has bad performance
+ void set_soft_transform(const Transform &p_transform);
+
+ void move_all_nodes(const Transform &p_transform);
+ void set_node_position(int node_index, const Vector3 &p_global_position);
+ void set_node_position(int node_index, const btVector3 &p_global_position);
+ void get_node_position(int node_index, Vector3 &r_position) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, Vector3 &r_offset) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, btVector3 &r_offset) const;
+
+ void set_node_mass(int node_index, btScalar p_mass);
+ btScalar get_node_mass(int node_index) const;
+ void reset_all_node_mass();
+ void reset_all_node_positions();
void set_activation_state(bool p_active);
- void set_mass(real_t p_val);
- _FORCE_INLINE_ real_t get_mass() const { return mass; }
- void set_stiffness(real_t p_val);
- _FORCE_INLINE_ real_t get_stiffness() const { return stiffness; }
+ void set_total_mass(real_t p_val);
+ _FORCE_INLINE_ real_t get_total_mass() const { return total_mass; }
+
+ void set_linear_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_linear_stiffness() const { return linear_stiffness; }
+
+ void set_areaAngular_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_areaAngular_stiffness() const { return areaAngular_stiffness; }
+
+ void set_volume_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_volume_stiffness() const { return volume_stiffness; }
+
void set_simulation_precision(int p_val);
_FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; }
+
void set_pressure_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; }
+
+ void set_pose_matching_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_pose_matching_coefficient() const { return pose_matching_coefficient; }
+
void set_damping_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; }
+
void set_drag_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; }
private:
- void reload_soft_body();
- void create_soft_body();
- void destroy_soft_body();
+ void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices);
+ void setup_soft_body();
+
+ void pin_node(int p_node_index);
+ void unpin_node(int p_node_index);
+ int search_node_pinned(int p_node_index) const;
};
#endif // SOFT_BODY_BULLET_H
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 971fd39509..132c3739d6 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -36,6 +36,7 @@
#include "constraint_bullet.h"
#include "godot_collision_configuration.h"
#include "godot_collision_dispatcher.h"
+#include "project_settings.h"
#include "rigid_body_bullet.h"
#include "servers/physics_server.h"
#include "soft_body_bullet.h"
@@ -325,7 +326,7 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_
}
}
-SpaceBullet::SpaceBullet(bool p_create_soft_world) :
+SpaceBullet::SpaceBullet() :
broadphase(NULL),
dispatcher(NULL),
solver(NULL),
@@ -338,7 +339,7 @@ SpaceBullet::SpaceBullet(bool p_create_soft_world) :
gravityMagnitude(10),
contactDebugCount(0) {
- create_empty_world(p_create_soft_world);
+ create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true));
direct_access = memnew(BulletPhysicsDirectSpaceState(this));
}
@@ -355,6 +356,7 @@ void SpaceBullet::flush_queries() {
}
void SpaceBullet::step(real_t p_delta_time) {
+ delta_time = p_delta_time;
dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
}
@@ -483,6 +485,7 @@ void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
void SpaceBullet::add_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
+ p_body->get_bt_soft_body()->m_worldInfo = get_soft_body_world_info();
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->addSoftBody(p_body->get_bt_soft_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
}
} else {
@@ -494,6 +497,7 @@ void SpaceBullet::remove_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->removeSoftBody(p_body->get_bt_soft_body());
+ p_body->get_bt_soft_body()->m_worldInfo = NULL;
}
}
}
@@ -549,7 +553,43 @@ BulletPhysicsDirectSpaceState *SpaceBullet::get_direct_state() {
}
btScalar calculateGodotCombinedRestitution(const btCollisionObject *body0, const btCollisionObject *body1) {
- return MAX(body0->getRestitution(), body1->getRestitution());
+
+ const PhysicsServer::CombineMode cm = static_cast<RigidBodyBullet *>(body0->getUserPointer())->get_restitution_combine_mode();
+
+ switch (cm) {
+ case PhysicsServer::COMBINE_MODE_INHERIT:
+ if (static_cast<RigidBodyBullet *>(body1->getUserPointer())->get_restitution_combine_mode() != PhysicsServer::COMBINE_MODE_INHERIT)
+ return calculateGodotCombinedRestitution(body1, body0);
+ // else use MAX [This is used when the two bodies doesn't use physical material]
+ case PhysicsServer::COMBINE_MODE_MAX:
+ return MAX(body0->getRestitution(), body1->getRestitution());
+ case PhysicsServer::COMBINE_MODE_MIN:
+ return MIN(body0->getRestitution(), body1->getRestitution());
+ case PhysicsServer::COMBINE_MODE_MULTIPLY:
+ return body0->getRestitution() * body1->getRestitution();
+ default: // Is always PhysicsServer::COMBINE_MODE_AVERAGE:
+ return (body0->getRestitution() + body1->getRestitution()) / 2;
+ }
+}
+
+btScalar calculateGodotCombinedFriction(const btCollisionObject *body0, const btCollisionObject *body1) {
+
+ const PhysicsServer::CombineMode cm = static_cast<RigidBodyBullet *>(body0->getUserPointer())->get_friction_combine_mode();
+
+ switch (cm) {
+ case PhysicsServer::COMBINE_MODE_INHERIT:
+ if (static_cast<RigidBodyBullet *>(body1->getUserPointer())->get_friction_combine_mode() != PhysicsServer::COMBINE_MODE_INHERIT)
+ return calculateGodotCombinedFriction(body1, body0);
+ // else use MULTIPLY [This is used when the two bodies doesn't use physical material]
+ case PhysicsServer::COMBINE_MODE_MULTIPLY:
+ return body0->getFriction() * body1->getFriction();
+ case PhysicsServer::COMBINE_MODE_MAX:
+ return MAX(body0->getFriction(), body1->getFriction());
+ case PhysicsServer::COMBINE_MODE_MIN:
+ return MIN(body0->getFriction(), body1->getFriction());
+ default: // Is always PhysicsServer::COMBINE_MODE_AVERAGE:
+ return (body0->getFriction() * body1->getFriction()) / 2;
+ }
}
void SpaceBullet::create_empty_world(bool p_create_soft_world) {
@@ -585,6 +625,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) {
ghostPairCallback = bulletnew(btGhostPairCallback);
godotFilterCallback = bulletnew(GodotFilterCallback);
gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution;
+ gCalculateCombinedFrictionCallback = &calculateGodotCombinedFriction;
dynamicsWorld->setWorldUserInfo(this);
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index a6c2786878..006c6462cf 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -84,7 +84,7 @@ public:
};
class SpaceBullet : public RIDBullet {
-private:
+
friend class AreaBullet;
friend void onBulletTickCallback(btDynamicsWorld *world, btScalar timeStep);
friend class BulletPhysicsDirectSpaceState;
@@ -109,12 +109,14 @@ private:
Vector<Vector3> contactDebug;
int contactDebugCount;
+ real_t delta_time;
public:
- SpaceBullet(bool p_create_soft_world);
+ SpaceBullet();
virtual ~SpaceBullet();
void flush_queries();
+ real_t get_delta_time() { return delta_time; }
void step(real_t p_delta_time);
_FORCE_INLINE_ btBroadphaseInterface *get_broadphase() { return broadphase; }
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index ba27d6839d..67cc7e1ba2 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1585,7 +1585,11 @@ CSGBrush *CSGPolygon::_build_brush() {
case MODE_PATH: {
float bl = curve->get_baked_length();
int splits = MAX(2, Math::ceil(bl / path_interval));
- face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ if (path_joined) {
+ face_count = splits * final_polygon.size() * 2;
+ } else {
+ face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ }
} break;
}
@@ -1793,8 +1797,14 @@ CSGBrush *CSGPolygon::_build_brush() {
float bl = curve->get_baked_length();
int splits = MAX(2, Math::ceil(bl / path_interval));
+ float u1 = 0.0;
+ float u2 = path_continuous_u ? 0.0 : 1.0;
- Transform path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
+ Transform path_to_this;
+ if (!path_local) {
+ // center on paths origin
+ path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
+ }
Transform prev_xf;
@@ -1812,6 +1822,9 @@ CSGBrush *CSGPolygon::_build_brush() {
for (int i = 0; i <= splits; i++) {
float ofs = i * path_interval;
+ if (i == splits && path_joined) {
+ ofs = 0.0;
+ }
Transform xf;
xf.origin = curve->interpolate_baked(ofs);
@@ -1836,6 +1849,11 @@ CSGBrush *CSGPolygon::_build_brush() {
xf = path_to_this * xf;
if (i > 0) {
+ if (path_continuous_u) {
+ u1 = u2;
+ u2 += (prev_xf.origin - xf.origin).length();
+ };
+
//put triangles where they belong
//add triangles for depth
for (int j = 0; j < final_polygon.size(); j++) {
@@ -1850,10 +1868,10 @@ CSGBrush *CSGPolygon::_build_brush() {
};
Vector2 u[4] = {
- Vector2(0, 0),
- Vector2(0, 1),
- Vector2(1, 1),
- Vector2(1, 0)
+ Vector2(u1, 0),
+ Vector2(u1, 1),
+ Vector2(u2, 1),
+ Vector2(u2, 0)
};
// face 1
@@ -1888,7 +1906,7 @@ CSGBrush *CSGPolygon::_build_brush() {
}
}
- if (i == 0) {
+ if (i == 0 && !path_joined) {
for (int j = 0; j < triangles.size(); j += 3) {
for (int k = 0; k < 3; k++) {
@@ -1905,7 +1923,7 @@ CSGBrush *CSGPolygon::_build_brush() {
}
}
- if (i == splits) {
+ if (i == splits && !path_joined) {
for (int j = 0; j < triangles.size(); j += 3) {
for (int k = 0; k < 3; k++) {
@@ -2003,6 +2021,15 @@ void CSGPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation);
ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation);
+ ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon::set_path_local);
+ ClassDB::bind_method(D_METHOD("is_path_local"), &CSGPolygon::is_path_local);
+
+ ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon::set_path_continuous_u);
+ ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon::is_path_continuous_u);
+
+ ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon::set_path_joined);
+ ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon::is_path_joined);
+
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material);
@@ -2020,9 +2047,12 @@ void CSGPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node"), "set_path_node", "get_path_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path"), "set_path_node", "get_path_node");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_path_interval", "get_path_interval");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
@@ -2067,6 +2097,15 @@ float CSGPolygon::get_depth() const {
return depth;
}
+void CSGPolygon::set_path_continuous_u(bool p_enable) {
+ path_continuous_u = p_enable;
+ _make_dirty();
+}
+
+bool CSGPolygon::is_path_continuous_u() const {
+ return path_continuous_u;
+}
+
void CSGPolygon::set_spin_degrees(const float p_spin_degrees) {
ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
spin_degrees = p_spin_degrees;
@@ -2119,6 +2158,26 @@ CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const {
return path_rotation;
}
+void CSGPolygon::set_path_local(bool p_enable) {
+ path_local = p_enable;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGPolygon::is_path_local() const {
+ return path_local;
+}
+
+void CSGPolygon::set_path_joined(bool p_enable) {
+ path_joined = p_enable;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGPolygon::is_path_joined() const {
+ return path_joined;
+}
+
void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) {
smooth_faces = p_smooth_faces;
_make_dirty();
@@ -2160,5 +2219,8 @@ CSGPolygon::CSGPolygon() {
smooth_faces = false;
path_interval = 1;
path_rotation = PATH_ROTATION_PATH;
+ path_local = false;
+ path_continuous_u = false;
+ path_joined = false;
path_cache = NULL;
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index cbb5c7e041..6898cdaf64 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -334,10 +334,13 @@ private:
NodePath path_node;
float path_interval;
PathRotation path_rotation;
+ bool path_local;
Node *path_cache;
bool smooth_faces;
+ bool path_continuous_u;
+ bool path_joined;
bool _is_editable_3d_polygon() const;
bool _has_editable_3d_polygon_no_depth() const;
@@ -375,6 +378,15 @@ public:
void set_path_rotation(PathRotation p_rotation);
PathRotation get_path_rotation() const;
+ void set_path_local(bool p_enable);
+ bool is_path_local() const;
+
+ void set_path_continuous_u(bool p_enable);
+ bool is_path_continuous_u() const;
+
+ void set_path_joined(bool p_enable);
+ bool is_path_joined() const;
+
void set_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml
index 80455fda80..5ec7b5089d 100644
--- a/modules/csg/doc_classes/CSGBox.xml
+++ b/modules/csg/doc_classes/CSGBox.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Box shape.
</brief_description>
<description>
+ This node allows you to create a box for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,12 +14,16 @@
</methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth">
+ Depth of the box measured from the center of the box.
</member>
<member name="height" type="float" setter="set_height" getter="get_height">
+ Height of the box measured from the center of the box.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the box.
</member>
<member name="width" type="float" setter="set_width" getter="get_width">
+ Width of the box measured from the center of the box.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml
index b2265d7703..1cfaa74b7d 100644
--- a/modules/csg/doc_classes/CSGCombiner.xml
+++ b/modules/csg/doc_classes/CSGCombiner.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.1">
<brief_description>
+ A CSG node that allows you to combine other CSG modifiers.
</brief_description>
<description>
+ For complex arrangements of shapes it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as their input to create the final shape.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml
index 0cab26ad3d..92b170ed1f 100644
--- a/modules/csg/doc_classes/CSGCylinder.xml
+++ b/modules/csg/doc_classes/CSGCylinder.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Cylinder shape.
</brief_description>
<description>
+ This node allows you to create a cylinder (or cone) for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,16 +14,22 @@
</methods>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone">
+ If true a cone is created, the [member radius] will only apply to one side.
</member>
<member name="height" type="float" setter="set_height" getter="get_height">
+ The height of the cylinder.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the cylinder.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius">
+ The radius of the cylinder.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides">
+ The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. When false the cylinder will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml
index e5c3e5ccf3..419214b7e6 100644
--- a/modules/csg/doc_classes/CSGMesh.xml
+++ b/modules/csg/doc_classes/CSGMesh.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
+ This CSG node allows you to use any mesh resource as a CSG shape provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
</description>
<tutorials>
</tutorials>
@@ -12,6 +14,7 @@
</methods>
<members>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ The mesh resource to use as a CSG shape.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml
index 379c512d6a..a33e5557cb 100644
--- a/modules/csg/doc_classes/CSGPolygon.xml
+++ b/modules/csg/doc_classes/CSGPolygon.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
+ This node takes a 2D polygon shape and extrudes it to create a 3D mesh.
</description>
<tutorials>
</tutorials>
@@ -12,38 +14,63 @@
</methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth">
+ Extrusion depth when [member mode] is [constant MODE_DEPTH].
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ Material to use for the resulting mesh.
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode">
+ Extrusion mode.
+ </member>
+ <member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u">
+ If true the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH].
</member>
<member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
+ Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH].
+ </member>
+ <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined">
+ If true the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH].
+ </member>
+ <member name="path_local" type="bool" setter="set_path_local" getter="is_path_local">
+ If false we extrude centered on our path, if true we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH].
</member>
<member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
+ The [Shape] object containing the path along which we extrude when [member mode] is [constant MODE_PATH].
</member>
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon.PathRotation">
+ The method by which each slice is rotated along the path when [member mode] is [constant MODE_PATH].
</member>
<member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon">
+ Point array that defines the shape that we'll extrude.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ Generates smooth normals so smooth shading is applied to our mesh.
</member>
<member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
+ Degrees to rotate our extrusion for each slice when [member mode] is [constant MODE_SPIN].
</member>
<member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
+ Number of extrusion when [member mode] is [constant MODE_SPIN].
</member>
</members>
<constants>
<constant name="MODE_DEPTH" value="0" enum="Mode">
+ Shape is extruded to [member depth].
</constant>
<constant name="MODE_SPIN" value="1" enum="Mode">
+ Shape is extruded by rotating it around an axis.
</constant>
<constant name="MODE_PATH" value="2" enum="Mode">
+ Shape is extruded along a path set by a [Shape] set in [member path_node].
</constant>
<constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
+ Slice is not rotated.
</constant>
<constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
+ Slice is rotated around the up vector of the path.
</constant>
<constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
+ Slice is rotate to match the path exactly.
</constant>
</constants>
</class>
diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml
index bf41c40f22..2591bab7e3 100644
--- a/modules/csg/doc_classes/CSGPrimitive.xml
+++ b/modules/csg/doc_classes/CSGPrimitive.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.1">
<brief_description>
+ Base class for CSG primitives.
</brief_description>
<description>
</description>
@@ -12,6 +13,7 @@
</methods>
<members>
<member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces">
+ Invert the faces of the mesh.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
index cf236a4207..90621b94f4 100644
--- a/modules/csg/doc_classes/CSGShape.xml
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.1">
<brief_description>
+ The CSG base class.
</brief_description>
<description>
+ This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
</description>
<tutorials>
</tutorials>
@@ -13,23 +15,29 @@
<return type="bool">
</return>
<description>
+ Returns true if this is a root shape and is thus the object that is rendered.
</description>
</method>
</methods>
<members>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
+ The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap">
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision">
+ Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden.
</member>
</members>
<constants>
<constant name="OPERATION_UNION" value="0" enum="Operation">
+ Geometry of both primitives is merged, intersecting geometry is removed.
</constant>
<constant name="OPERATION_INTERSECTION" value="1" enum="Operation">
+ Only intersecting geometry remains, the rest is removed.
</constant>
<constant name="OPERATION_SUBTRACTION" value="2" enum="Operation">
+ The second shape is susbtracted from the first, leaving a dent with it's shape.
</constant>
</constants>
</class>
diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml
index 520368506e..a0069879cb 100644
--- a/modules/csg/doc_classes/CSGSphere.xml
+++ b/modules/csg/doc_classes/CSGSphere.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Sphere shape.
</brief_description>
<description>
+ This node allows you to create a sphere for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,14 +14,19 @@
</methods>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the sphere.
</member>
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments">
+ Number of vertical slices for the sphere.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius">
+ Radius of the sphere.
</member>
<member name="rings" type="int" setter="set_rings" getter="get_rings">
+ Number of horizontal slices for the sphere.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the sphere are set to give a smooth effect making the sphere seem rounded. When false the sphere will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml
index 58bbef2600..187d71a2fa 100644
--- a/modules/csg/doc_classes/CSGTorus.xml
+++ b/modules/csg/doc_classes/CSGTorus.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Torus shape.
</brief_description>
<description>
+ This node allows you to create a torus for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,16 +14,22 @@
</methods>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius">
+ The inner radius of the torus.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the torus.
</member>
<member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius">
+ The outer radius of the torus.
</member>
<member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides">
+ The number of edges each ring of the torus is constructed of.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides">
+ The number of slices the torus is constructed of.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the torus are set to give a smooth effect making the torus seem rounded. When false the torus will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 92fb41adf8..116a86b27b 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -13,6 +13,7 @@ gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cp
gdn_env.Append(CPPPATH=['#modules/gdnative/include/'])
+SConscript("net/SCsub")
SConscript("arvr/SCsub")
SConscript("pluginscript/SCsub")
@@ -196,7 +197,7 @@ def build_gdnative_api_struct(target, source, env):
with open(source.path, 'w') as fd:
fd.write(_build_gdnative_api_struct_source(api))
-_, gensource = gdn_env.Command(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'],
+_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'],
'gdnative_api.json', build_gdnative_api_struct)
gdn_env.add_source_files(env.modules_sources, [gensource])
@@ -277,7 +278,7 @@ def build_gdnative_wrapper_code(target, source, env):
if ARGUMENTS.get('gdnative_wrapper', False):
#build wrapper code
- gensource, = gdn_env.Command('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code)
+ gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code)
gd_wrapper_env = env.Clone()
gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/'])
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 626e9239f8..c5b37d35b4 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -10,6 +10,7 @@ def get_doc_classes():
"GDNative",
"GDNativeLibrary",
"NativeScript",
+ "PacketPeerGDNative",
"PluginScript",
]
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
new file mode 100644
index 0000000000..bfa688592d
--- /dev/null
+++ b/modules/gdnative/include/net/godot_net.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* godot_net.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_NATIVENET_H
+#define GODOT_NATIVENET_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For future versions of the API we should only add new functions at the end of the structure and use the
+// version info to detect whether a call is available
+
+// Use these to populate version in your plugin
+#define GODOT_NET_API_MAJOR 3
+#define GODOT_NET_API_MINOR 1
+
+typedef struct {
+
+ godot_gdnative_api_version version; /* version of our API */
+ godot_object *data; /* User reference */
+
+ /* This is StreamPeer */
+ godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes);
+ godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int &r_received);
+ godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes);
+ godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int &r_sent);
+
+ int (*get_available_bytes)(const void *user);
+
+ void *next; /* For extension? */
+} godot_net_stream_peer;
+
+/* Binds a StreamPeerGDNative to the provided interface */
+void godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ void *next; /* For extension? */
+} godot_net_packet_peer;
+
+/* Binds a PacketPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ /* This is NetworkedMultiplayerPeer */
+ void (*set_transfer_mode)(void *, godot_int);
+ godot_int (*get_transfer_mode)(const void *);
+ // 0 = broadcast, 1 = server, <0 = all but abs(value)
+ void (*set_target_peer)(void *, godot_int);
+ godot_int (*get_packet_peer)(const void *);
+ godot_bool (*is_server)(const void *);
+ void (*poll)(void *);
+ // Must be > 0, 1 is for server
+ int32_t (*get_unique_id)(const void *);
+ void (*set_refuse_new_connections)(void *, godot_bool);
+ godot_bool (*is_refusing_new_connections)(const void *);
+ godot_int (*get_connection_status)(const void *);
+
+ void *next; /* For extension? Or maybe not... */
+} godot_net_multiplayer_peer;
+
+/* Binds a MultiplayerPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index d6abbc1bcf..5e093109d5 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -697,11 +697,21 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
godot_variant result;
+
+#ifdef DEBUG_ENABLED
+ current_method_call = p_method;
+#endif
+
result = E->get().method.method((godot_object *)owner,
E->get().method.method_data,
userdata,
p_argcount,
(godot_variant **)p_args);
+
+#ifdef DEBUG_ENABLED
+ current_method_call = "";
+#endif
+
Variant res = *(Variant *)&result;
godot_variant_destroy(&result);
r_error.error = Variant::CallError::CALL_OK;
@@ -716,6 +726,15 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
}
void NativeScriptInstance::notification(int p_notification) {
+#ifdef DEBUG_ENABLED
+ if (p_notification == MainLoop::NOTIFICATION_CRASH) {
+ if (current_method_call != StringName("")) {
+ ERR_PRINTS("NativeScriptInstance detected crash on method: " + current_method_call);
+ current_method_call = "";
+ }
+ }
+#endif
+
Variant value = p_notification;
const Variant *args[1] = { &value };
call_multilevel("_notification", args, 1);
@@ -1034,7 +1053,7 @@ Ref<Script> NativeScriptLanguage::get_template(const String &p_class_name, const
s->set_class_name(p_class_name);
return Ref<NativeScript>(s);
}
-bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const {
return true;
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index b47962dc37..1b39b63ad9 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -181,6 +181,9 @@ class NativeScriptInstance : public ScriptInstance {
Object *owner;
Ref<NativeScript> script;
+#ifdef DEBUG_ENABLED
+ StringName current_method_call;
+#endif
void _ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount);
@@ -292,7 +295,7 @@ public:
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub
new file mode 100644
index 0000000000..53f9271128
--- /dev/null
+++ b/modules/gdnative/net/SCsub
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import os
+import methods
+
+Import('env')
+Import('env_modules')
+
+env_net_gdnative = env_modules.Clone()
+
+env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
+env_net_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
new file mode 100644
index 0000000000..e2d710b5ad
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_peer_gdnative.h"
+
+MultiplayerPeerGDNative::MultiplayerPeerGDNative() {
+ interface = NULL;
+}
+
+MultiplayerPeerGDNative::~MultiplayerPeerGDNative() {
+}
+
+void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) {
+ interface = p_interface;
+}
+
+Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
+}
+
+Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
+}
+
+int MultiplayerPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
+}
+
+/* NetworkedMultiplayerPeer */
+void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_transfer_mode(interface->data, (godot_int)p_mode);
+}
+
+NetworkedMultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
+ ERR_FAIL_COND_V(interface == NULL, TRANSFER_MODE_UNRELIABLE);
+ return (TransferMode)interface->get_transfer_mode(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_target_peer(interface->data, p_peer_id);
+}
+
+int MultiplayerPeerGDNative::get_packet_peer() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_packet_peer(interface->data);
+}
+
+bool MultiplayerPeerGDNative::is_server() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->is_server(interface->data);
+}
+
+void MultiplayerPeerGDNative::poll() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->poll(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_unique_id() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_unique_id(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_refuse_new_connections(interface->data, p_enable);
+}
+
+bool MultiplayerPeerGDNative::is_refusing_new_connections() const {
+ ERR_FAIL_COND_V(interface == NULL, true);
+ return interface->is_refusing_new_connections(interface->data);
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const {
+ ERR_FAIL_COND_V(interface == NULL, CONNECTION_DISCONNECTED);
+ return (ConnectionStatus)interface->get_connection_status(interface->data);
+}
+
+void MultiplayerPeerGDNative::_bind_methods() {
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) {
+
+ ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl);
+}
+}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
new file mode 100644
index 0000000000..c8c95b3dd7
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_PEER_GDNATIVE_H
+#define MULTIPLAYER_PEER_GDNATIVE_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer {
+ GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer)
+
+protected:
+ static void _bind_methods();
+ const godot_net_multiplayer_peer *interface;
+
+public:
+ MultiplayerPeerGDNative();
+ ~MultiplayerPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
+
+ /* Specific to PacketPeer */
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const;
+ virtual int get_available_packet_count() const;
+
+ /* Specific to NetworkedMultiplayerPeer */
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual TransferMode get_transfer_mode() const;
+ virtual void set_target_peer(int p_peer_id);
+
+ virtual int get_packet_peer() const;
+
+ virtual bool is_server() const;
+
+ virtual void poll();
+
+ virtual int get_unique_id() const;
+
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual ConnectionStatus get_connection_status() const;
+};
+
+#endif // MULTIPLAYER_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp
new file mode 100644
index 0000000000..ceae79edc0
--- /dev/null
+++ b/modules/gdnative/net/packet_peer_gdnative.cpp
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* packet_peer_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "packet_peer_gdnative.h"
+
+PacketPeerGDNative::PacketPeerGDNative() {
+ interface = NULL;
+}
+
+PacketPeerGDNative::~PacketPeerGDNative() {
+}
+
+void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) {
+ interface = p_impl;
+}
+
+void PacketPeerGDNative::_bind_methods() {
+}
+
+Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
+}
+
+Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
+}
+
+int PacketPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
+}
+
+int PacketPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) {
+
+ ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl);
+}
+}
diff --git a/modules/webm/resource_importer_webm.h b/modules/gdnative/net/packet_peer_gdnative.h
index d61e6e2a93..71814177ed 100644
--- a/modules/webm/resource_importer_webm.h
+++ b/modules/gdnative/net/packet_peer_gdnative.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.h */
+/* packet_peer_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,32 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTERWEBM_H
-#define RESOURCEIMPORTERWEBM_H
+#ifndef PACKET_PEER_GDNATIVE_H
+#define PACKET_PEER_GDNATIVE_H
-#include "io/resource_import.h"
+#include "core/io/packet_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
-class ResourceImporterWebm : public ResourceImporter {
- GDCLASS(ResourceImporterWebm, ResourceImporter)
-public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual String get_save_extension() const;
- virtual String get_resource_type() const;
+class PacketPeerGDNative : public PacketPeer {
+ GDCLASS(PacketPeerGDNative, PacketPeer)
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+protected:
+ static void _bind_methods();
+ const godot_net_packet_peer *interface;
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+public:
+ PacketPeerGDNative();
+ ~PacketPeerGDNative();
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL);
+ /* Sets the interface implementation from GDNative */
+ void set_native_packet_peer(const godot_net_packet_peer *p_impl);
- ResourceImporterWebm();
+ /* Specific to PacketPeer */
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const;
+ virtual int get_available_packet_count() const;
};
-#endif // RESOURCEIMPORTERWEBM_H
+#endif // PACKET_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp
new file mode 100644
index 0000000000..c3fb4d8008
--- /dev/null
+++ b/modules/gdnative/net/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "multiplayer_peer_gdnative.h"
+#include "packet_peer_gdnative.h"
+#include "stream_peer_gdnative.h"
+
+void register_net_types() {
+ ClassDB::register_class<MultiplayerPeerGDNative>();
+ ClassDB::register_class<PacketPeerGDNative>();
+ ClassDB::register_class<StreamPeerGDNative>();
+}
+
+void unregister_net_types() {
+}
diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h
new file mode 100644
index 0000000000..9545a2ba8f
--- /dev/null
+++ b/modules/gdnative/net/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_net_types();
+void unregister_net_types();
diff --git a/modules/webm/resource_importer_webm.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp
index 7124a503e8..4d1f2ec9a5 100644
--- a/modules/webm/resource_importer_webm.cpp
+++ b/modules/gdnative/net/stream_peer_gdnative.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.cpp */
+/* stream_peer_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,69 +28,50 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_webm.h"
+#include "stream_peer_gdnative.h"
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-#include "video_stream_webm.h"
-
-String ResourceImporterWebm::get_importer_name() const {
-
- return "Webm";
+StreamPeerGDNative::StreamPeerGDNative() {
+ interface = NULL;
}
-String ResourceImporterWebm::get_visible_name() const {
-
- return "Webm";
+StreamPeerGDNative::~StreamPeerGDNative() {
}
-void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("webm");
+void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) {
+ interface = p_interface;
}
-String ResourceImporterWebm::get_save_extension() const {
- return "webmstr";
+void StreamPeerGDNative::_bind_methods() {
}
-String ResourceImporterWebm::get_resource_type() const {
-
- return "VideoStreamWebm";
+Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_data(interface->data, p_data, p_bytes));
}
-bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
+Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, r_sent));
}
-int ResourceImporterWebm::get_preset_count() const {
- return 0;
+Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_data(interface->data, p_buffer, p_bytes));
}
-String ResourceImporterWebm::get_preset_name(int p_idx) const {
- return String();
+Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, r_received));
}
-void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+int StreamPeerGDNative::get_available_bytes() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_bytes(interface->data);
}
-Error ResourceImporterWebm::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) {
-
- FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ);
- if (!f) {
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
- }
- memdelete(f);
-
- VideoStreamWebm *stream = memnew(VideoStreamWebm);
- stream->set_file(p_source_file);
+extern "C" {
- Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
-
- return ResourceSaver::save(p_save_path + ".webmstr", webm_stream);
+void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface) {
+ ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface);
}
-
-ResourceImporterWebm::ResourceImporterWebm() {
}
diff --git a/modules/theora/resource_importer_theora.h b/modules/gdnative/net/stream_peer_gdnative.h
index e3c79287ad..654234e6ab 100644
--- a/modules/theora/resource_importer_theora.h
+++ b/modules/gdnative/net/stream_peer_gdnative.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_theora.h */
+/* stream_peer_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,34 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTEROGGTHEORA_H
-#define RESOURCEIMPORTEROGGTHEORA_H
+#ifndef STREAM_PEER_GDNATIVE_H
+#define STREAM_PEER_GDNATIVE_H
-#include "video_stream_theora.h"
+#include "core/io/stream_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
-#include "core/io/resource_import.h"
+class StreamPeerGDNative : public StreamPeer {
-class ResourceImporterTheora : public ResourceImporter {
- GDCLASS(ResourceImporterTheora, ResourceImporter)
-public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual String get_save_extension() const;
- virtual String get_resource_type() const;
-
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+ GDCLASS(StreamPeerGDNative, StreamPeer);
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+protected:
+ static void _bind_methods();
+ godot_net_stream_peer *interface;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL);
-
- ResourceImporterTheora();
+public:
+ StreamPeerGDNative();
+ ~StreamPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_stream_peer(godot_net_stream_peer *p_interface);
+
+ /* Specific to StreamPeer */
+ Error put_data(const uint8_t *p_data, int p_bytes);
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
+ Error get_data(uint8_t *p_buffer, int p_bytes);
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+ int get_available_bytes() const;
};
-#endif // RESOURCEIMPORTEROGGTHEORA_H
+#endif // STREAM_PEER_GDNATIVE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index 8018178bd5..816b0f0cab 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -108,7 +108,7 @@ Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const
return script;
}
-bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const {
PoolStringArray functions;
if (_desc.validate) {
bool ret = _desc.validate(
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 709345885b..2443e31361 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -74,7 +74,7 @@ public:
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index a0b6fbeb75..d18297f2f8 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -38,6 +38,7 @@
#include "arvr/register_types.h"
#include "nativescript/register_types.h"
+#include "net/register_types.h"
#include "pluginscript/register_types.h"
#include "core/engine.h"
@@ -321,6 +322,7 @@ void register_gdnative_types() {
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
+ register_net_types();
register_arvr_types();
register_nativescript_types();
register_pluginscript_types();
@@ -379,6 +381,7 @@ void unregister_gdnative_types() {
unregister_pluginscript_types();
unregister_nativescript_types();
unregister_arvr_types();
+ unregister_net_types();
memdelete(GDNativeCallRegistry::singleton);
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index fedc510f01..f2a1a5b50c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -75,9 +75,11 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_keyword = false;
bool in_word = false;
bool in_function_name = false;
+ bool in_variable_declaration = false;
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
+ bool expect_type = false;
Color keyword_color;
Color color;
@@ -205,6 +207,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (str[k] == '(') {
in_function_name = true;
+ } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_VAR)) {
+ in_variable_declaration = true;
}
}
@@ -222,6 +226,28 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (is_symbol) {
in_function_name = false;
in_member_variable = false;
+
+ if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') {
+ expect_type = false;
+ }
+ if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
+ expect_type = true;
+ }
+
+ if (in_variable_declaration || previous_text == "(" || previous_text == ",") {
+ int k = j;
+ // Skip space
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == ':') {
+ // has type hint
+ expect_type = true;
+ }
+ }
+
+ in_variable_declaration = false;
}
if (!in_node_path && in_region == -1 && str[j] == '$') {
@@ -256,6 +282,9 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
} else if (is_number) {
next_type = NUMBER;
color = number_color;
+ } else if (expect_type) {
+ next_type = TYPE;
+ color = type_color;
} else {
next_type = IDENTIFIER;
}
@@ -330,6 +359,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
+ type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
}
SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 0296ab7652..b8cb4a65e9 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -44,7 +44,8 @@ private:
FUNCTION,
KEYWORD,
MEMBER,
- IDENTIFIER
+ IDENTIFIER,
+ TYPE,
};
// colours
@@ -56,6 +57,7 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
+ Color type_color;
public:
static SyntaxHighlighter *create();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index f23e7854a5..8bd29ffc55 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -220,16 +220,14 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
+ GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- PropertyInfo arg;
- arg.type = Variant::NIL; //variant
- arg.name = E->get()->get_argument_name(i);
- mi.arguments.push_back(arg);
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ mi.arguments.push_back(func->get_argument_type(i));
}
- mi.return_val.name = "Variant";
+ mi.return_val = func->get_return_type();
p_list->push_back(mi);
}
}
@@ -277,16 +275,14 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
if (!E)
return MethodInfo();
+ GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- PropertyInfo arg;
- arg.type = Variant::NIL; //variant
- arg.name = E->get()->get_argument_name(i);
- mi.arguments.push_back(arg);
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ mi.arguments.push_back(func->get_argument_type(i));
}
- mi.return_val.name = "Variant";
+ mi.return_val = func->get_return_type();
return mi;
}
@@ -941,8 +937,12 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (err.error == Variant::CallError::CALL_OK) {
return true; //function exists, call was successful
}
- } else
+ } else {
+ if (!E->get().data_type.is_type(p_value)) {
+ return false; // Type mismatch
+ }
members[E->get().index] = p_value;
+ }
return true;
}
}
@@ -1735,10 +1735,13 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"NAN",
"self",
"true",
+ "void",
// functions
+ "as",
"assert",
"breakpoint",
"class",
+ "class_name",
"extends",
"is",
"func",
@@ -1788,6 +1791,82 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
+
+ return p_type == "GDScript";
+}
+
+String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type) const {
+
+ PoolVector<uint8_t> sourcef;
+ Error err;
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (err) {
+ return String();
+ }
+
+ int len = f->get_len();
+ sourcef.resize(len + 1);
+ PoolVector<uint8_t>::Write w = sourcef.write();
+ int r = f->get_buffer(w.ptr(), len);
+ f->close();
+ memdelete(f);
+ ERR_FAIL_COND_V(r != len, String());
+ w[len] = 0;
+
+ String s;
+ if (s.parse_utf8((const char *)w.ptr())) {
+ return String();
+ }
+
+ GDScriptParser parser;
+
+ parser.parse(s, p_path.get_base_dir(), true, p_path);
+
+ if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
+
+ const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
+ if (r_base_type) {
+ GDScriptParser::DataType base_type;
+ if (c->base_type.has_type) {
+ base_type = c->base_type;
+ while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ } else {
+ base_type = GDScriptParser::DataType();
+ }
+ } break;
+ default: {
+ base_type = GDScriptParser::DataType();
+ } break;
+ }
+ }
+ }
+ if (base_type.has_type) {
+ *r_base_type = base_type.native_type;
+ } else {
+ // Fallback
+ if (c->extends_used && c->extends_class.size() == 1) {
+ *r_base_type = c->extends_class[0];
+ } else if (!c->extends_used) {
+ *r_base_type = "Reference";
+ }
+ }
+ }
+ return c->name;
+ }
+
+ return String();
+}
+
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index a35b0a10d5..d5fe7a000b 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -64,6 +64,7 @@ class GDScript : public Script {
StringName setter;
StringName getter;
MultiplayerAPI::RPCMode rpc_mode;
+ GDScriptDataType data_type;
};
friend class GDScriptInstance;
@@ -145,8 +146,13 @@ public:
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; }
const Set<StringName> &get_members() const { return members; }
+ const GDScriptDataType &get_member_type(const StringName &p_member) const {
+ ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType());
+ return member_indices[p_member].data_type;
+ }
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
+ const String &get_script_class_name() const { return name; }
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
@@ -391,7 +397,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -439,6 +445,11 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ /* GLOBAL CLASSES */
+
+ virtual bool handles_global_class_type(const String &p_type) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const;
+
GDScriptLanguage();
~GDScriptLanguage();
};
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5c834966c5..a428ccd306 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -34,7 +34,7 @@
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
- if (!codegen.function_node || codegen.function_node->_static)
+ if (codegen.function_node && codegen.function_node->_static)
return false;
if (codegen.stack_identifiers.has(p_name))
@@ -111,23 +111,50 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
return true;
}
-/*
-int GDScriptCompiler::_parse_subexpression(CodeGen& codegen,const GDScriptParser::Node *p_expression) {
-
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
+ if (!p_datatype.has_type) {
+ return GDScriptDataType();
+ }
- int ret = _parse_expression(codegen,p_expression);
- if (ret<0)
- return ret;
+ GDScriptDataType result;
+ result.has_type = true;
- if (ret&(GDScriptFunction::ADDR_TYPE_STACK<<GDScriptFunction::ADDR_BITS)) {
- codegen.stack_level++;
- codegen.check_max_stack_level();
- //stack was used, keep value
+ switch (p_datatype.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ result.kind = GDScriptDataType::BUILTIN;
+ result.builtin_type = p_datatype.builtin_type;
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ result.kind = GDScriptDataType::NATIVE;
+ result.native_type = p_datatype.native_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ result.kind = GDScriptDataType::SCRIPT;
+ result.script_type = p_datatype.script_type;
+ result.native_type = result.script_type->get_instance_base_type();
+ }
+ case GDScriptParser::DataType::GDSCRIPT: {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = p_datatype.script_type;
+ result.native_type = result.script_type->get_instance_base_type();
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ if (p_datatype.class_type->name == StringName()) {
+ result.script_type = Ref<GDScript>(main_script);
+ } else {
+ result.script_type = class_map[p_datatype.class_type->name];
+ }
+ result.native_type = result.script_type->get_instance_base_type();
+ } break;
+ default: {
+ ERR_PRINT("Parser bug: converting unresolved type.");
+ result.has_type = false;
+ }
}
- return ret;
+ return result;
}
-*/
int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
@@ -263,21 +290,47 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
owner = owner->_owner;
}
- /*
- handled in constants now
- if (codegen.script->subclasses.has(identifier)) {
- //same with a subclass, make it a local constant.
- int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]);
- return idx|(GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
-
- }*/
-
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
}
+ /* TRY GLOBAL CLASSES */
+
+ if (ScriptServer::is_global_class(identifier)) {
+
+ const GDScriptParser::ClassNode *class_node = codegen.class_node;
+ while (class_node->owner) {
+ class_node = class_node->owner;
+ }
+
+ if (class_node->name == identifier) {
+ _set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression);
+ return -1;
+ }
+
+ RES res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ return -1;
+ }
+
+ Variant key = res;
+ int idx;
+
+ if (!codegen.constant_map.has(key)) {
+
+ idx = codegen.constant_map.size();
+ codegen.constant_map[key] = idx;
+
+ } else {
+ idx = codegen.constant_map[key];
+ }
+
+ return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ }
+
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
@@ -395,6 +448,83 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return dst_addr;
} break;
+ case GDScriptParser::Node::TYPE_CAST: {
+ const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
+
+ int slevel = p_stack_level;
+ int src_addr = _parse_expression(codegen, cn->source_node, slevel);
+ if (src_addr < 0)
+ return src_addr;
+ if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ switch (cn->cast_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
+ codegen.opcodes.push_back(cn->cast_type.builtin_type);
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (cn->cast_type.class_type->name == StringName()) {
+ script = codegen.script;
+ } else {
+ StringName name = cn->cast_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = cn->cast_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ default: {
+ _set_error("Parser bug: unresolved data type.", cn);
+ return -1;
+ }
+ }
+
+ codegen.opcodes.push_back(src_addr); // source adddress
+ int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
+ } break;
case GDScriptParser::Node::TYPE_OPERATOR: {
//hell breaks loose
@@ -747,14 +877,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) return -1;
} break;
- case GDScriptParser::OperatorNode::OP_PREINC: {
- } break; //?
- case GDScriptParser::OperatorNode::OP_PREDEC: {
- } break;
- case GDScriptParser::OperatorNode::OP_INC: {
- } break;
- case GDScriptParser::OperatorNode::OP_DEC: {
- } break;
//binary operators (in precedence order)
case GDScriptParser::OperatorNode::OP_IN: {
if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) return -1;
@@ -828,7 +950,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (on->arguments[0]->type == GDScriptParser::Node::TYPE_OPERATOR && (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX || static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED)) {
- // SET (chained) MODE!
+ // SET (chained) MODE!
#ifdef DEBUG_ENABLED
if (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
const GDScriptParser::OperatorNode *inon = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
@@ -1029,12 +1151,87 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (src_address_b < 0)
return -1;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
+
+ if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
+ // Typed assignment
+ switch (assign_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
+ codegen.opcodes.push_back(assign_type.builtin_type); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (assign_type.class_type->name == StringName()) {
+ script = codegen.script;
+ } else {
+ StringName name = assign_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = assign_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
+ }
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
return dst_address_a; //if anything, returns wathever was assigned or correct stack position
}
-
} break;
case GDScriptParser::OperatorNode::OP_IS: {
@@ -1490,6 +1687,18 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) {
gdfunc->_static = p_func->_static;
gdfunc->rpc_mode = p_func->rpc_mode;
+ gdfunc->argument_types.resize(p_func->argument_types.size());
+ for (int i = 0; i < p_func->argument_types.size(); i++) {
+ gdfunc->argument_types[i] = _gdtype_from_datatype(p_func->argument_types[i]);
+ }
+ gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
+ } else {
+ gdfunc->_static = false;
+ gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ gdfunc->return_type = GDScriptDataType();
+ gdfunc->return_type.has_type = true;
+ gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
+ gdfunc->return_type.builtin_type = Variant::NIL;
}
#ifdef TOOLS_ENABLED
@@ -1618,12 +1827,23 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
-Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
-
- Map<StringName, Ref<GDScript> > old_subclasses;
+Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- if (p_keep_state) {
- old_subclasses = p_script->subclasses;
+ if (p_class->owner && p_class->owner->owner) {
+ // Owner is not root
+ StringName owner_name = p_class->owner->name;
+ if (!parsed_classes.has(owner_name)) {
+ if (parsing_classes.has(owner_name)) {
+ _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ parsing_classes.insert(owner_name);
+ Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state);
+ if (err) {
+ return err;
+ }
+ parsing_classes.erase(owner_name);
+ }
}
p_script->native = Ref<GDScriptNativeClass>();
@@ -1647,236 +1867,100 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
Ref<GDScriptNativeClass> native;
- if (p_class->extends_used) {
- //do inheritance
- String path = p_class->extends_file;
-
- Ref<GDScript> script;
-
- if (path != "") {
- //path (and optionally subclasses)
-
- if (path.is_rel_path()) {
-
- String base;
-
- if (p_owner) {
- GDScript *current_class = p_owner;
- while (current_class != NULL) {
- base = current_class->get_path();
- if (base == "")
- current_class = current_class->_owner;
- else
- break;
- }
- } else {
- base = p_script->get_path();
- }
-
- if (base == "" || base.is_rel_path()) {
- _set_error("Could not resolve relative path for parent class: " + path, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- path = base.get_base_dir().plus_file(path).simplify_path();
- }
- script = ResourceLoader::load(path);
- if (script.is_null()) {
- _set_error("Could not load base class: " + path, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- if (!script->valid) {
-
- _set_error("Script not fully loaded (cyclic preload?): " + path, p_class);
- return ERR_BUSY;
- }
- //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
-
- if (p_class->extends_class.size()) {
-
- for (int i = 0; i < p_class->extends_class.size(); i++) {
-
- String sub = p_class->extends_class[i];
- if (script->subclasses.has(sub)) {
-
- Ref<Script> subclass = script->subclasses[sub]; //avoid reference from disappearing
- script = subclass;
- } else {
-
- _set_error("Could not find subclass: " + sub, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- }
- }
-
- } else {
-
- ERR_FAIL_COND_V(p_class->extends_class.size() == 0, ERR_BUG);
- //look around for the subclasses
-
- String base = p_class->extends_class[0];
- GDScript *p = p_owner;
- Ref<GDScript> base_class;
-
- while (p) {
-
- if (p->subclasses.has(base)) {
-
- base_class = p->subclasses[base];
- break;
- }
-
- if (p->constants.has(base)) {
-
- base_class = p->constants[base];
- if (base_class.is_null()) {
- _set_error("Constant is not a class: " + base, p_class);
- return ERR_SCRIPT_FAILED;
- }
- break;
- }
-
- p = p->_owner;
- }
-
- if (base_class.is_valid()) {
-
- String ident = base;
-
- for (int i = 1; i < p_class->extends_class.size(); i++) {
-
- String subclass = p_class->extends_class[i];
-
- ident += ("." + subclass);
-
- if (base_class->subclasses.has(subclass)) {
-
- base_class = base_class->subclasses[subclass];
- } else if (base_class->constants.has(subclass)) {
-
- Ref<GDScript> new_base_class = base_class->constants[subclass];
- if (new_base_class.is_null()) {
- _set_error("Constant is not a class: " + ident, p_class);
- return ERR_SCRIPT_FAILED;
- }
- base_class = new_base_class;
- } else {
-
- _set_error("Could not find subclass: " + ident, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- }
-
- script = base_class;
-
- } else {
-
- if (p_class->extends_class.size() > 1) {
-
- _set_error("Invalid inheritance (unknown class+subclasses)", p_class);
- return ERR_FILE_NOT_FOUND;
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
-
- _set_error("Unknown class: '" + base + "'", p_class);
- return ERR_FILE_NOT_FOUND;
+ // Inheritance
+ switch (p_class->base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ StringName base_name = p_class->base_type.class_type->name;
+ // Make sure dependency is parsed first
+ if (!parsed_classes.has(base_name)) {
+ if (parsing_classes.has(base_name)) {
+ _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
+ return ERR_PARSE_ERROR;
}
-
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- if (!native.is_valid()) {
-
- _set_error("Global not a class: '" + base + "'", p_class);
-
- return ERR_FILE_NOT_FOUND;
+ parsing_classes.insert(base_name);
+ Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
}
+ parsing_classes.erase(base_name);
}
- }
-
- if (script.is_valid()) {
-
- p_script->base = script;
+ Ref<GDScript> base = class_map[base_name];
+ p_script->base = base;
p_script->_base = p_script->base.ptr();
- p_script->member_indices = script->member_indices;
-
- } else if (native.is_valid()) {
-
+ p_script->member_indices = base->member_indices;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> base = p_class->base_type.script_type;
+ p_script->base = base;
+ p_script->_base = p_script->base.ptr();
+ p_script->member_indices = base->member_indices;
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
+ ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
- } else {
-
- _set_error("Could not determine inheritance", p_class);
- return ERR_FILE_NOT_FOUND;
- }
-
- } else {
- // without extends, implicitly extend Reference
- int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"];
- native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
- ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
- p_script->native = native;
+ } break;
+ default: {
+ _set_error("Parser bug: invalid inheritance.", p_class);
+ return ERR_BUG;
+ } break;
}
- //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
-
for (int i = 0; i < p_class->variables.size(); i++) {
StringName name = p_class->variables[i].identifier;
- if (p_script->member_indices.has(name)) {
- _set_error("Member '" + name + "' already exists (in current or parent class)", p_class);
- return ERR_ALREADY_EXISTS;
- }
- if (_is_class_member_property(p_script, name)) {
- _set_error("Member '" + name + "' already exists as a class property.", p_class);
- return ERR_ALREADY_EXISTS;
- }
- if (p_class->variables[i]._export.type != Variant::NIL) {
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+ minfo.setter = p_class->variables[i].setter;
+ minfo.getter = p_class->variables[i].getter;
+ minfo.rpc_mode = p_class->variables[i].rpc_mode;
+ minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
+
+ PropertyInfo prop_info = minfo.data_type;
+ prop_info.name = name;
+ PropertyInfo export_info = p_class->variables[i]._export;
- p_script->member_info[name] = p_class->variables[i]._export;
+ if (export_info.type != Variant::NIL) {
+
+ if (!minfo.data_type.has_type) {
+ prop_info.type = export_info.type;
+ prop_info.class_name = export_info.class_name;
+ }
+ prop_info.hint = export_info.hint;
+ prop_info.hint_string = export_info.hint_string;
+ prop_info.usage = export_info.usage;
#ifdef TOOLS_ENABLED
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
-
p_script->member_default_values[name] = p_class->variables[i].default_value;
}
#endif
} else {
-
- p_script->member_info[name] = PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+ prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
- //int new_idx = p_script->member_indices.size();
- GDScript::MemberInfo minfo;
- minfo.index = p_script->member_indices.size();
- minfo.setter = p_class->variables[i].setter;
- minfo.getter = p_class->variables[i].getter;
- minfo.rpc_mode = p_class->variables[i].rpc_mode;
-
+ p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = p_class->variables[i].line;
#endif
}
- for (int i = 0; i < p_class->constant_expressions.size(); i++) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- StringName name = p_class->constant_expressions[i].identifier;
- ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
+ StringName name = E->key();
- if (_is_class_member_property(p_script, name)) {
- _set_error("Member '" + name + "' already exists as a class property.", p_class);
- return ERR_ALREADY_EXISTS;
- }
+ ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
- GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(p_class->constant_expressions[i].expression);
+ GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
p_script->constants.insert(name, constant->value);
-//p_script->constants[constant->value].make_const();
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->constant_expressions[i].expression->line;
+ p_script->member_lines[name] = E->get().expression->line;
#endif
}
@@ -1909,23 +1993,27 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->_signals[name] = p_class->_signals[i].arguments;
}
+
+ if (p_class->name != StringName()) {
+ parsed_classes.insert(p_class->name);
+ }
+
//parse sub-classes
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
- Ref<GDScript> subclass;
+ Ref<GDScript> subclass = class_map[name];
- if (old_subclasses.has(name)) {
- subclass = old_subclasses[name];
- } else {
- subclass.instance();
+ // Subclass might still be parsing, just skip it
+ if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
+ parsing_classes.insert(name);
+ Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
+ if (err)
+ return err;
+ parsing_classes.erase(name);
}
- Error err = _parse_class(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
- if (err)
- return err;
-
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = p_class->subclasses[i]->line;
@@ -1935,6 +2023,10 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->subclasses.insert(name, subclass);
}
+ return OK;
+}
+
+Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
bool has_initializer = false;
@@ -1975,44 +2067,6 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#ifdef DEBUG_ENABLED
- //validate setters/getters if debug is enabled
- for (int i = 0; i < p_class->variables.size(); i++) {
-
- if (p_class->variables[i].setter) {
- const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].setter);
- if (!E) {
- _set_error("Setter function '" + String(p_class->variables[i].setter) + "' not found in class.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
-
- if (E->get()->is_static()) {
-
- _set_error("Setter function '" + String(p_class->variables[i].setter) + "' is static.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
- }
- if (p_class->variables[i].getter) {
- const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].getter);
- if (!E) {
- _set_error("Getter function '" + String(p_class->variables[i].getter) + "' not found in class.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
-
- if (E->get()->is_static()) {
-
- _set_error("Getter function '" + String(p_class->variables[i].getter) + "' is static.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
- }
- }
//validate instances if keeping state
@@ -2065,22 +2119,67 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#endif
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ StringName name = p_class->subclasses[i]->name;
+ Ref<GDScript> subclass = class_map[name];
+
+ Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ if (err) {
+ return err;
+ }
+ }
+
p_script->valid = true;
return OK;
}
+void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+
+ Map<StringName, Ref<GDScript> > old_subclasses;
+
+ if (p_keep_state) {
+ old_subclasses = p_script->subclasses;
+ }
+
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ StringName name = p_class->subclasses[i]->name;
+
+ Ref<GDScript> subclass;
+
+ if (old_subclasses.has(name)) {
+ subclass = old_subclasses[name];
+ } else {
+ subclass.instance();
+ }
+
+ subclass->_owner = const_cast<GDScript *>(p_script);
+ class_map.insert(name, subclass);
+
+ _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ }
+}
+
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
error = "";
parser = p_parser;
+ main_script = p_script;
const GDScriptParser::Node *root = parser->get_parse_tree();
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
source = p_script->get_path();
- Error err = _parse_class(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ // Create scripts for subclasses beforehand so they can be referenced
+ _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+
+ Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+
+ if (err)
+ return err;
+
+ err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 237b0de9e7..55f5e2b48e 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,12 +31,17 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
+#include "core/set.h"
#include "gdscript.h"
#include "gdscript_parser.h"
class GDScriptCompiler {
const GDScriptParser *parser;
+ Map<StringName, Ref<GDScript> > class_map;
+ Set<StringName> parsed_classes;
+ Set<StringName> parsing_classes;
+ GDScript *main_script;
struct CodeGen {
GDScript *script;
@@ -138,11 +143,15 @@ class GDScriptCompiler {
bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
+
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
- Error _parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 0e4724fec2..2e4a4c40dd 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -37,6 +37,7 @@
#include "os/file_access.h"
#ifdef TOOLS_ENABLED
+#include "core/reference.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#include "engine.h"
@@ -53,21 +54,45 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("' '");
}
Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
+#ifdef TOOLS_ENABLED
+ bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false);
+#else
+ bool th = false;
+#endif
- String _template = String() +
- "extends %BASE%\n\n" +
- "# class member variables go here, for example:\n" +
- "# var a = 2\n" +
- "# var b = \"textvar\"\n\n" +
- "func _ready():\n" +
- "%TS%# Called when the node is added to the scene for the first time.\n" +
- "%TS%# Initialization here.\n" +
- "%TS%pass\n\n" +
- "#func _process(delta):\n" +
- "#%TS%# Called every frame. Delta is time since last frame.\n" +
- "#%TS%# Update game logic here.\n" +
+ String _template = "extends %BASE%\n"
+ "\n"
+ "# Declare member variables here. Examples:\n"
+ "# var a %INT_TYPE%= 2\n"
+ "# var b %STRING_TYPE%= \"text\"\n"
+ "\n"
+ "# Called when the node enters the scene tree for the first time.\n"
+ "func _ready()%VOID_RETURN%:\n"
+ "%TS%pass # Replace with function body.\n"
+ "\n"
+ "# Called every frame. 'delta' is the elapsed time since the previous frame.\n"
+ "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n"
"#%TS%pass\n";
+#ifdef TOOLS_ENABLED
+ if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) {
+ _template = _template.replace("%INT_TYPE%", ": int ");
+ _template = _template.replace("%STRING_TYPE%", ": String ");
+ _template = _template.replace("%FLOAT_TYPE%", " : float");
+ _template = _template.replace("%VOID_RETURN%", " -> void");
+ } else {
+ _template = _template.replace("%INT_TYPE%", "");
+ _template = _template.replace("%STRING_TYPE%", "");
+ _template = _template.replace("%FLOAT_TYPE%", "");
+ _template = _template.replace("%VOID_RETURN%", "");
+ }
+#else
+ _template = _template.replace("%INT_TYPE%", "");
+ _template = _template.replace("%STRING_TYPE%", "");
+ _template = _template.replace("%FLOAT_TYPE%", "");
+ _template = _template.replace("%VOID_RETURN%", "");
+#endif
+
_template = _template.replace("%BASE%", p_base_class_name);
_template = _template.replace("%TS%", _get_indentation());
@@ -91,11 +116,11 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p
p_script->set_source_code(src);
}
-bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const {
GDScriptParser parser;
- Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path);
+ Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines);
if (err) {
r_line_error = parser.get_error_line();
r_col_error = parser.get_error_column();
@@ -415,63 +440,34 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_cons
String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+#ifdef TOOLS_ENABLED
+ bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");
+#else
+ bool th = false;
+#endif
+
String s = "func " + p_name + "(";
if (p_args.size()) {
for (int i = 0; i < p_args.size(); i++) {
if (i > 0)
s += ", ";
s += p_args[i].get_slice(":", 0);
+ if (th) {
+ String type = p_args[i].get_slice(":", 1);
+ if (!type.empty() && type != "var") {
+ s += " : " + type;
+ }
+ }
}
}
- s += "):\n" + _get_indentation() + "pass # Replace with function body.\n";
+ s += String(")") + (th ? " -> void" : "") + ":\n" + _get_indentation() + "pass # Replace with function body.\n";
return s;
}
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-
-struct GDScriptCompletionIdentifier {
-
- String enumeration;
- StringName obj_type;
- Ref<GDScript> script;
- Variant::Type type;
- Variant value; //im case there is a value, also return it
+//////// COMPLETION //////////
- GDScriptCompletionIdentifier() :
- type(Variant::NIL) {}
-};
-
-static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) {
-
- GDScriptCompletionIdentifier t;
- t.type = p_variant.get_type();
- t.value = p_variant;
- if (p_variant.get_type() == Variant::OBJECT) {
- Object *obj = p_variant;
- if (obj) {
-
- if (p_allow_gdnative_class && Object::cast_to<GDScriptNativeClass>(obj)) {
- t.obj_type = Object::cast_to<GDScriptNativeClass>(obj)->get_name();
- t.value = Variant();
- } else {
-
- t.obj_type = obj->get_class();
- }
- }
- }
- return t;
-}
-
-static GDScriptCompletionIdentifier _get_type_from_pinfo(const PropertyInfo &p_info) {
-
- GDScriptCompletionIdentifier t;
- t.type = p_info.type;
- if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- t.obj_type = p_info.hint_string;
- }
- return t;
-}
+#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
struct GDScriptCompletionContext {
@@ -480,1043 +476,1612 @@ struct GDScriptCompletionContext {
const GDScriptParser::BlockNode *block;
Object *base;
String base_path;
-};
-
-static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) {
-
- if (context._class->extends_used) {
- //do inheritance
- String path = context._class->extends_file;
-
- Ref<GDScript> script;
- Ref<GDScriptNativeClass> native;
+ int line;
- if (path != "") {
- //path (and optionally subclasses)
-
- if (path.is_rel_path()) {
-
- path = context.base_path.plus_file(path);
- }
-
- if (ScriptCodeCompletionCache::get_singleton())
- script = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
- else
- script = ResourceLoader::load(path);
+ GDScriptCompletionContext() :
+ _class(NULL),
+ function(NULL),
+ block(NULL),
+ base(NULL) {}
+};
- if (script.is_null()) {
- return REF();
- }
- if (!script->is_valid()) {
+struct GDScriptCompletionIdentifier {
+ GDScriptParser::DataType type;
+ String enumeration;
+ Variant value;
+ const GDScriptParser::Node *assigned_expression;
- return REF();
- }
- //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
+ GDScriptCompletionIdentifier() :
+ assigned_expression(NULL) {}
+};
- if (context._class->extends_class.size()) {
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) {
- for (int i = 0; i < context._class->extends_class.size(); i++) {
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ r_list.insert("\"" + p_dir->get_file_path(i) + "\"");
+ }
- String sub = context._class->extends_class[i];
- if (script->get_subclasses().has(sub)) {
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _get_directory_contents(p_dir->get_subdir(i), r_list);
+ }
+}
- script = script->get_subclasses()[sub];
- } else {
+static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = true) {
- return REF();
- }
- }
- }
+ if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ String enum_name = p_info.class_name;
+ if (enum_name.find(".") == -1) {
+ return enum_name;
+ }
+ return enum_name.get_slice(".", 1);
+ }
- if (script.is_valid())
- return script;
+ String n = p_info.name;
+ int idx = n.find(":");
+ if (idx != -1) {
+ return n.substr(idx + 1, n.length());
+ }
+ if (p_info.type == Variant::OBJECT) {
+ if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ return p_info.hint_string;
} else {
+ return p_info.class_name.operator String();
+ }
+ }
+ if (p_info.type == Variant::NIL) {
+ if (p_isarg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ return "var";
+ } else {
+ return "void";
+ }
+ }
- if (context._class->extends_class.size() == 0) {
- ERR_PRINT("BUG");
- return REF();
- }
-
- String base = context._class->extends_class[0];
-
- if (context._class->extends_class.size() > 1) {
-
- return REF();
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
+ return Variant::get_type_name(p_info.type);
+}
- return REF();
+static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
+ GDScriptCompletionIdentifier ci;
+ ci.value = p_value;
+ ci.type.is_constant = true;
+ ci.type.has_type = true;
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ ci.type.builtin_type = p_value.get_type();
+
+ if (ci.type.builtin_type == Variant::OBJECT) {
+ Object *obj = p_value.operator Object *();
+ if (!obj) {
+ return ci;
+ }
+ ci.type.native_type = obj->get_class_name();
+ Ref<Script> scr = p_value;
+ if (scr.is_valid()) {
+ ci.type.is_meta_type = true;
+ } else {
+ ci.type.is_meta_type = false;
+ scr = obj->get_script();
+ }
+ if (scr.is_valid()) {
+ ci.type.script_type = scr;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } else {
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
}
-
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- return native;
+ ci.type.native_type = scr->get_instance_base_type();
+ } else {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
}
}
- return Ref<Reference>();
+ return ci;
}
-static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) {
+static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) {
+ GDScriptCompletionIdentifier ci;
- GDScriptCompletionIdentifier id;
-
- REF pc = _get_parent_class(context);
- if (!pc.is_valid()) {
- return id;
+ if (p_property.type == Variant::NIL) {
+ // Variant
+ return ci;
}
- Ref<GDScriptNativeClass> nc = pc;
- Ref<GDScript> s = pc;
- if (s.is_null() && nc.is_null()) {
- return id;
- }
- while (!s.is_null()) {
- nc = s->get_native();
- s = s->get_base();
- }
- if (nc.is_null()) {
- return id;
+ if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ ci.enumeration = p_property.class_name;
}
- id.type = Variant::OBJECT;
- if (context.base)
- id.value = context.base;
- id.obj_type = nc->get_name();
- return id;
+ ci.type.has_type = true;
+ ci.type.builtin_type = p_property.type;
+ if (p_property.type == Variant::OBJECT) {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
+ ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ } else {
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ }
+ return ci;
}
-static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type, bool p_for_indexing);
-
-static bool _guess_expression_type(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, GDScriptCompletionIdentifier &r_type, bool p_for_indexing = false) {
-
- if (p_node->type == GDScriptParser::Node::TYPE_CONSTANT) {
-
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_node);
+static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_gdtype) {
+ GDScriptCompletionIdentifier ci;
+ if (!p_gdtype.has_type) {
+ return ci;
+ }
- r_type = _get_type_from_variant(cn->value);
+ ci.type.has_type = true;
+ ci.type.builtin_type = p_gdtype.builtin_type;
+ ci.type.native_type = p_gdtype.native_type;
+ ci.type.script_type = p_gdtype.script_type;
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_DICTIONARY) {
-
- r_type.type = Variant::DICTIONARY;
+ switch (p_gdtype.kind) {
+ case GDScriptDataType::BUILTIN: {
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ } break;
+ case GDScriptDataType::NATIVE: {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
+ } break;
+ case GDScriptDataType::GDSCRIPT: {
+ ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } break;
+ case GDScriptDataType::SCRIPT: {
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
+ } break;
+ }
+ return ci;
+}
- //what the heck, fill it anyway
- const GDScriptParser::DictionaryNode *an = static_cast<const GDScriptParser::DictionaryNode *>(p_node);
- Dictionary d;
- for (int i = 0; i < an->elements.size(); i++) {
- GDScriptCompletionIdentifier k;
- if (_guess_expression_type(context, an->elements[i].key, p_line, k) && k.value.get_type() != Variant::NIL) {
- GDScriptCompletionIdentifier v;
- if (_guess_expression_type(context, an->elements[i].value, p_line, v)) {
- d[k.value] = v.value;
+static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
+
+static bool _guess_expression_type(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) {
+ bool found = false;
+ switch (p_expression->type) {
+ case GDScriptParser::Node::TYPE_CONSTANT: {
+ const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression);
+ r_type = _type_from_variant(cn->value);
+ found = true;
+ } break;
+ case GDScriptParser::Node::TYPE_SELF: {
+ if (p_context._class) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ r_type.type.is_constant = true;
+ r_type.value = p_context.base;
+ found = true;
+ }
+ } break;
+ case GDScriptParser::Node::TYPE_IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
+ found = _guess_identifier_type(p_context, id->name, r_type);
+ } break;
+ case GDScriptParser::Node::TYPE_DICTIONARY: {
+ // Try to recreate the dictionary
+ const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
+ Dictionary d;
+ bool full = true;
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (_guess_expression_type(p_context, dn->elements[i].key, key)) {
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, dn->elements[i].value, value)) {
+ if (!value.type.is_constant) {
+ full = false;
+ break;
+ }
+ d[key.value] = value.value;
+ } else {
+ full = false;
+ break;
+ }
+ } else {
+ full = false;
+ break;
}
}
- }
- r_type.value = d;
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_ARRAY) {
-
- r_type.type = Variant::ARRAY;
- //what the heck, fill it anyway
- const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_node);
- Array arr;
- arr.resize(an->elements.size());
- for (int i = 0; i < an->elements.size(); i++) {
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, an->elements[i], p_line, ci)) {
- arr[i] = ci.value;
+ if (full) {
+ // If not fully constant, setting this value is detrimental to the inference
+ r_type.value = d;
+ r_type.type.is_constant = true;
}
- }
- r_type.value = arr;
- return true;
-
- } else if (p_node->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
-
- MethodInfo mi = GDScriptFunctions::get_info(static_cast<const GDScriptParser::BuiltInFunctionNode *>(p_node)->function);
- r_type = _get_type_from_pinfo(mi.return_val);
-
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- return _guess_identifier_type(context, p_line - 1, static_cast<const GDScriptParser::IdentifierNode *>(p_node)->name, r_type, p_for_indexing);
- } else if (p_node->type == GDScriptParser::Node::TYPE_SELF) {
- //eeh...
-
- r_type = _get_native_class(context);
- return r_type.type != Variant::NIL;
-
- } else if (p_node->type == GDScriptParser::Node::TYPE_OPERATOR) {
-
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->op == GDScriptParser::OperatorNode::OP_CALL) {
-
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
-
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
- r_type.type = tn->vtype;
- return true;
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
-
- const GDScriptParser::BuiltInFunctionNode *bin = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- return _guess_expression_type(context, bin, p_line, r_type);
-
- } else if (op->arguments.size() > 1 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
-
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && String(id) == "new") {
-
- //shortcut
- StringName identifier = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
-
- if (ClassDB::class_exists(identifier)) {
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = identifier;
- return true;
- }
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::DICTIONARY;
+ } break;
+ case GDScriptParser::Node::TYPE_ARRAY: {
+ // Try to recreate the array
+ const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
+ Array a;
+ bool full = true;
+ a.resize(an->elements.size());
+ for (int i = 0; i < an->elements.size(); i++) {
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, an->elements[i], value)) {
+ a[i] = value.value;
+ } else {
+ full = false;
+ break;
}
-
- GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, op->arguments[0], p_line, base))
- return false;
-
- if (base.type == Variant::OBJECT) {
-
- if (id.operator String() == "new" && base.value.get_type() == Variant::OBJECT) {
-
- Object *obj = base.value;
- if (obj && Object::cast_to<GDScriptNativeClass>(obj)) {
- GDScriptNativeClass *gdnc = Object::cast_to<GDScriptNativeClass>(obj);
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = gdnc->get_name();
- return true;
- } else {
-
- if (base.obj_type != StringName()) {
-
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = base.obj_type;
- return true;
- }
+ }
+ if (full) {
+ // If not fully constant, setting this value is detrimental to the inference
+ r_type.value = a;
+ }
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::ARRAY;
+ } break;
+ case GDScriptParser::Node::TYPE_OPERATOR: {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_expression);
+ switch (op->op) {
+ case GDScriptParser::OperatorNode::OP_CALL: {
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = tn->vtype;
+ found = true;
+ break;
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
+ const GDScriptParser::BuiltInFunctionNode *bin = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(bin->function);
+ r_type = _type_from_property(mi.return_val);
+ found = true;
+ break;
+ } else if (op->arguments.size() >= 2 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
+
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- }
- if (ClassDB::has_method(base.obj_type, id)) {
-
-#ifdef TOOLS_ENABLED
- MethodBind *mb = ClassDB::get_method(base.obj_type, id);
- PropertyInfo pi = mb->get_return_info();
+ // Try call if constant methods with constant arguments
+ if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) {
+ GDScriptParser::DataType native_type = base.type;
- //try calling the function if constant and all args are constant, should not crash..
- Object *baseptr = base.value;
-
- if (mb->is_const() && pi.type == Variant::OBJECT) {
-
- bool all_valid = true;
- Vector<Variant> args;
- for (int i = 2; i < op->arguments.size(); i++) {
- GDScriptCompletionIdentifier arg;
+ while (native_type.kind == GDScriptParser::DataType::CLASS) {
+ native_type = native_type.class_type->base_type;
+ }
- if (_guess_expression_type(context, op->arguments[i], p_line, arg)) {
- if (arg.value.get_type() != Variant::NIL && arg.value.get_type() != Variant::OBJECT) { // calling with object seems dangerous, i don' t know
- args.push_back(arg.value);
+ while (native_type.kind == GDScriptParser::DataType::GDSCRIPT || native_type.kind == GDScriptParser::DataType::SCRIPT) {
+ if (native_type.script_type.is_valid()) {
+ Ref<Script> parent = native_type.script_type->get_base_script();
+ if (parent.is_valid()) {
+ native_type.script_type = parent;
} else {
- all_valid = false;
- break;
+ native_type.kind = GDScriptParser::DataType::NATIVE;
+ native_type.native_type = native_type.script_type->get_instance_base_type();
+ if (!ClassDB::class_exists(native_type.native_type)) {
+ native_type.native_type = String("_") + native_type.native_type;
+ if (!ClassDB::class_exists(native_type.native_type)) {
+ native_type.has_type = false;
+ }
+ }
}
- } else {
- all_valid = false;
}
}
- if (all_valid && String(id) == "get_node" && ClassDB::is_parent_class(base.obj_type, "Node") && args.size()) {
-
- String arg1 = args[0];
- if (arg1.begins_with("/root/")) {
- String which = arg1.get_slice("/", 2);
- if (which != "") {
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- //print_line("find singleton");
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
- String s = E->get().name;
- if (!s.begins_with("autoload/"))
- continue;
- //print_line("found "+s);
- String name = s.get_slice("/", 1);
- //print_line("name: "+name+", which: "+which);
- if (name == which) {
- String script = ProjectSettings::get_singleton()->get(s);
-
- if (!script.begins_with("res://")) {
- script = "res://" + script;
- }
-
- if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
- script = script.get_basename() + ".gd";
- }
-
- if (FileAccess::exists(script)) {
-
- //print_line("is a script");
-
- Ref<Script> scr;
- if (ScriptCodeCompletionCache::get_singleton())
- scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
- else
- scr = ResourceLoader::load(script);
-
- r_type.obj_type = "Node";
- r_type.type = Variant::OBJECT;
- r_type.script = scr;
- r_type.value = Variant();
-
- return true;
- }
+ if (native_type.has_type && native_type.kind == GDScriptParser::DataType::NATIVE) {
+ MethodBind *mb = ClassDB::get_method(native_type.native_type, id);
+ if (mb && mb->is_const()) {
+ bool all_is_const = true;
+ Vector<Variant> args;
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+ for (int i = 2; all_is_const && i < op->arguments.size(); i++) {
+ GDScriptCompletionIdentifier arg;
+
+ if (_guess_expression_type(c, op->arguments[i], arg)) {
+ if (arg.type.has_type && arg.type.is_constant && arg.value.get_type() != Variant::OBJECT) {
+ args.push_back(arg.value);
+ } else {
+ all_is_const = false;
}
+ } else {
+ all_is_const = false;
}
}
- }
- }
- if (baseptr) {
-
- if (all_valid) {
- Vector<const Variant *> argptr;
- for (int i = 0; i < args.size(); i++) {
- argptr.push_back(&args[i]);
+ Object *baseptr = base.value;
+
+ if (all_is_const && String(id) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
+
+ String arg1 = args[0];
+ if (arg1.begins_with("/root/")) {
+ String which = arg1.get_slice("/", 2);
+ if (which != "") {
+ // Try singletons first
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
+ found = true;
+ } else {
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ if (name == which) {
+ String script = ProjectSettings::get_singleton()->get(s);
+
+ if (script.begins_with("*")) {
+ script = script.right(1);
+ }
+
+ if (!script.begins_with("res://")) {
+ script = "res://" + script;
+ }
+
+ if (!script.ends_with(".gd")) {
+ //not a script, try find the script anyway,
+ //may have some success
+ script = script.get_basename() + ".gd";
+ }
+
+ if (FileAccess::exists(script)) {
+ Ref<Script> scr;
+ if (ScriptCodeCompletionCache::get_singleton()) {
+ scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
+ } else {
+ scr = ResourceLoader::load(script);
+ }
+ if (scr.is_valid()) {
+ r_type.type.has_type = true;
+ r_type.type.script_type = scr;
+ r_type.type.is_constant = false;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ r_type.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } else {
+ r_type.type.kind = GDScriptParser::DataType::SCRIPT;
+ }
+ r_type.value = Variant();
+ found = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
}
- Variant::CallError ce;
- Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
-
- if (ce.error == Variant::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ if (!found && all_is_const && baseptr) {
+ Vector<const Variant *> argptr;
+ for (int i = 0; i < args.size(); i++) {
+ argptr.push_back(&args[i]);
+ }
- if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != NULL) {
+ Variant::CallError ce;
+ Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
- r_type = _get_type_from_variant(ret);
- return true;
+ if (ce.error == Variant::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != NULL) {
+ r_type = _type_from_variant(ret);
+ found = true;
+ }
}
}
}
}
}
- r_type.type = pi.type;
- if (pi.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- r_type.obj_type = pi.hint_string;
+ if (!found) {
+ found = _guess_method_return_type_from_base(c, base, id, r_type);
}
-
- return true;
-#else
- return false;
-#endif
- } else {
- return false;
}
- } else {
- //method for some variant..
- Variant::CallError ce;
- Variant v = Variant::construct(base.type, NULL, 0, ce);
- List<MethodInfo> mi;
- v.get_method_list(&mi);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
-
- if (!E->get().name.begins_with("_") && E->get().name == id.operator String()) {
-
- MethodInfo mi = E->get();
- r_type.type = mi.return_val.type;
- if (mi.return_val.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- r_type.obj_type = mi.return_val.hint_string;
- }
- return true;
- }
+ } break;
+ case GDScriptParser::OperatorNode::OP_PARENT_CALL: {
+ if (!p_context._class || !op->arguments.size() || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ break;
}
- }
- }
- } else if (op->op == GDScriptParser::OperatorNode::OP_INDEX || op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- GDScriptCompletionIdentifier p1;
- GDScriptCompletionIdentifier p2;
+ StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
- if (op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
+ GDScriptCompletionIdentifier base;
+ base.value = p_context.base;
+ base.type = p_context._class->base_type;
- if (op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- String id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
- p2.type = Variant::STRING;
- p2.value = id;
- }
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- } else {
- if (op->arguments[1]) {
- if (!_guess_expression_type(context, op->arguments[1], p_line, p2)) {
-
- return false;
+ found = _guess_method_return_type_from_base(c, base, id, r_type);
+ } break;
+ case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ found = false;
+ break;
}
- }
- }
-
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_ARRAY) {
-
- const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(op->arguments[0]);
- if (p2.value.is_num()) {
- int index = p2.value;
- if (index < 0 || index >= an->elements.size())
- return false;
- return _guess_expression_type(context, an->elements[index], p_line, r_type);
- }
-
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
-
- const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- if (p2.value.get_type() == Variant::NIL)
- return false;
-
- for (int i = 0; i < dn->elements.size(); i++) {
-
- GDScriptCompletionIdentifier k;
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- if (!_guess_expression_type(context, dn->elements[i].key, p_line, k)) {
-
- return false;
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- if (k.value.get_type() == Variant::NIL)
- return false;
+ if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(id->name))) {
+ Variant value = base.value.operator Dictionary()[String(id->name)];
+ r_type = _type_from_variant(value);
+ found = true;
+ break;
+ }
- if (k.value == p2.value) {
+ const GDScriptParser::DictionaryNode *dn = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
+ }
- return _guess_expression_type(context, dn->elements[i].value, p_line, r_type);
+ if (dn) {
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (!_guess_expression_type(c, dn->elements[i].key, key)) {
+ continue;
+ }
+ if (key.value == String(id->name)) {
+ r_type.assigned_expression = dn->elements[i].value;
+ found = _guess_expression_type(c, dn->elements[i].value, r_type);
+ break;
+ }
+ }
}
- }
- } else {
+ if (!found) {
+ found = _guess_identifier_type_from_base(c, base, id->name, r_type);
+ }
+ } break;
+ case GDScriptParser::OperatorNode::OP_INDEX: {
+ if (op->arguments.size() < 2) {
+ found = false;
+ break;
+ }
- if (op->arguments[0]) {
- if (!_guess_expression_type(context, op->arguments[0], p_line, p1)) {
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- return false;
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- }
- if (p1.value.get_type() == Variant::OBJECT) {
- //??
-
- if (p1.obj_type != StringName() && p2.type == Variant::STRING) {
+ GDScriptCompletionIdentifier index;
+ if (!_guess_expression_type(c, op->arguments[1], index)) {
+ found = false;
+ break;
+ }
- StringName base_type = p1.obj_type;
+ if (base.value.in(index.value)) {
+ Variant value = base.value.get(index.value);
+ r_type = _type_from_variant(value);
+ found = true;
+ break;
+ }
- if (p1.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = p1.value;
- if (gdn.is_valid()) {
+ // Look if it is a dictionary node
+ const GDScriptParser::DictionaryNode *dn = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
+ }
- base_type = gdn->get_name();
+ if (dn) {
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (!_guess_expression_type(c, dn->elements[i].key, key)) {
+ continue;
}
- }
- StringName index = p2.value;
- bool valid;
- Variant::Type t = ClassDB::get_property_type(base_type, index, &valid);
- if (t != Variant::NIL && valid) {
- r_type.type = t;
- if (t == Variant::INT || t == Variant::OBJECT) {
-//check for enum!
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-
- StringName getter = ClassDB::get_property_getter(base_type, index);
- if (getter != StringName()) {
- MethodBind *mb = ClassDB::get_method(base_type, getter);
- if (mb) {
- PropertyInfo rt = mb->get_return_info();
- if ((rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && t == Variant::INT) {
- r_type.enumeration = rt.class_name;
- } else if (t == Variant::OBJECT) {
-
- r_type.obj_type = rt.class_name;
- }
- }
- }
-#endif
+ if (key.value == index.value) {
+ r_type.assigned_expression = dn->elements[i].value;
+ found = _guess_expression_type(p_context, dn->elements[i].value, r_type);
+ break;
}
-
- return true;
}
}
- } else if (p1.value.get_type() != Variant::NIL) {
- bool valid;
- Variant ret = p1.value.get(p2.value, &valid);
- if (valid) {
- r_type = _get_type_from_variant(ret);
- return true;
+ // Look if it is an array node
+ if (!found && index.value.is_num()) {
+ int idx = index.value;
+ const GDScriptParser::ArrayNode *an = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_ARRAY) {
+ an = static_cast<const GDScriptParser::ArrayNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_ARRAY) {
+ an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);
+ }
+
+ if (an && idx >= 0 && an->elements.size() > idx) {
+ r_type.assigned_expression = an->elements[idx];
+ found = _guess_expression_type(c, an->elements[idx], r_type);
+ break;
+ }
}
- } else {
- if (p1.type != Variant::NIL) {
- Variant::CallError ce;
- Variant base = Variant::construct(p1.type, NULL, 0, ce);
- bool valid;
- Variant ret = base.get(p2.value, &valid);
+ // Look for valid indexing in other types
+ if (!found && (index.value.get_type() == Variant::STRING || index.value.get_type() == Variant::NODE_PATH)) {
+ StringName id = index.value;
+ found = _guess_identifier_type_from_base(c, base, id, r_type);
+ } else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
+ Variant::CallError err;
+ Variant base_val = Variant::construct(base.type.builtin_type, NULL, 0, err);
+ bool valid = false;
+ Variant res = base_val.get(index.value, &valid);
if (valid) {
- r_type = _get_type_from_variant(ret);
- return true;
+ r_type = _type_from_variant(res);
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ found = true;
}
}
- }
- }
+ } break;
+ default: {
+ if (op->arguments.size() < 2) {
+ found = false;
+ break;
+ }
- } else {
+ Variant::Operator vop = Variant::OP_MAX;
+ switch (op->op) {
+ case GDScriptParser::OperatorNode::OP_ADD: vop = Variant::OP_ADD; break;
+ case GDScriptParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBTRACT; break;
+ case GDScriptParser::OperatorNode::OP_MUL: vop = Variant::OP_MULTIPLY; break;
+ case GDScriptParser::OperatorNode::OP_DIV: vop = Variant::OP_DIVIDE; break;
+ case GDScriptParser::OperatorNode::OP_MOD: vop = Variant::OP_MODULE; break;
+ case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: vop = Variant::OP_SHIFT_LEFT; break;
+ case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: vop = Variant::OP_SHIFT_RIGHT; break;
+ case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break;
+ case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break;
+ case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break;
+ default: {}
+ }
- Variant::Operator vop = Variant::OP_MAX;
- switch (op->op) {
- case GDScriptParser::OperatorNode::OP_ADD: vop = Variant::OP_ADD; break;
- case GDScriptParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBTRACT; break;
- case GDScriptParser::OperatorNode::OP_MUL: vop = Variant::OP_MULTIPLY; break;
- case GDScriptParser::OperatorNode::OP_DIV: vop = Variant::OP_DIVIDE; break;
- case GDScriptParser::OperatorNode::OP_MOD: vop = Variant::OP_MODULE; break;
- case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: vop = Variant::OP_SHIFT_LEFT; break;
- case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: vop = Variant::OP_SHIFT_RIGHT; break;
- case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break;
- case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break;
- case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break;
- default: {}
- }
-
- if (vop == Variant::OP_MAX)
- return false;
+ if (vop == Variant::OP_MAX) {
+ break;
+ }
- GDScriptCompletionIdentifier p1;
- GDScriptCompletionIdentifier p2;
+ GDScriptCompletionContext context = p_context;
+ context.line = op->line;
- if (op->arguments[0]) {
- if (!_guess_expression_type(context, op->arguments[0], p_line, p1)) {
+ GDScriptCompletionIdentifier p1;
+ GDScriptCompletionIdentifier p2;
- return false;
- }
- }
+ if (!_guess_expression_type(context, op->arguments[0], p1)) {
+ found = false;
+ break;
+ }
- if (op->arguments.size() > 1) {
- if (!_guess_expression_type(context, op->arguments[1], p_line, p2)) {
+ if (!_guess_expression_type(context, op->arguments[1], p2)) {
+ found = false;
+ break;
+ }
- return false;
- }
- }
+ Variant::CallError ce;
+ bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
+ Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, NULL, 0, ce);
+ bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
+ Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, NULL, 0, ce);
+ // avoid potential invalid ops
+ if ((vop == Variant::OP_DIVIDE || vop == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
+ v2 = 1;
+ v2_use_value = false;
+ }
+ if (vop == Variant::OP_DIVIDE && v2.get_type() == Variant::REAL) {
+ v2 = 1.0;
+ v2_use_value = false;
+ }
- Variant::CallError ce;
- bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type, NULL, 0, ce);
- bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type, NULL, 0, ce);
- // avoid potential invalid ops
- if ((vop == Variant::OP_DIVIDE || vop == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
- v2 = 1;
- v2_use_value = false;
- }
- if (vop == Variant::OP_DIVIDE && v2.get_type() == Variant::REAL) {
- v2 = 1.0;
- v2_use_value = false;
+ Variant res;
+ bool valid;
+ Variant::evaluate(vop, v1, v2, res, valid);
+ if (!valid) {
+ found = false;
+ break;
+ }
+ r_type = _type_from_variant(res);
+ if (!v1_use_value || !v2_use_value) {
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ }
+
+ found = true;
+ } break;
}
+ } break;
+ }
- Variant r;
- bool valid;
- Variant::evaluate(vop, v1, v2, r, valid);
- if (!valid)
- return false;
- r_type.type = r.get_type();
- if (v1_use_value && v2_use_value)
- r_type.value = r;
+ // It may have found a null, but that's never useful
+ if (found && r_type.type.has_type && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {
+ found = false;
+ }
- return true;
+ // Check type hint last. For collections we want chance to get the actual value first
+ // This way we can detect types from the content of dictionaries and arrays
+ if (!found && p_expression->get_datatype().has_type) {
+ r_type.type = p_expression->get_datatype();
+ if (!r_type.assigned_expression) {
+ r_type.assigned_expression = p_expression;
}
+ found = true;
}
- return false;
+ return found;
}
-static bool _guess_identifier_type_in_block(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
- if (context.block->if_condition && context.block->if_condition->type == GDScriptParser::Node::TYPE_OPERATOR && static_cast<const GDScriptParser::OperatorNode *>(context.block->if_condition)->op == GDScriptParser::OperatorNode::OP_IS) {
- //is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
- //super dirty hack, but very useful
- //credit: Zylann
- //TODO: this could be hacked to detect ANDed conditions too..
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(context.block->if_condition);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name == p_identifier) {
- //bingo
- if (_guess_expression_type(context, op->arguments[1], op->line, r_type)) {
- return true;
+ // Look in blocks first
+ const GDScriptParser::BlockNode *blk = p_context.block;
+ int last_assign_line = -1;
+ const GDScriptParser::Node *last_assigned_expression = NULL;
+ GDScriptParser::DataType var_type;
+ while (blk) {
+ if (blk->variables.has(p_identifier)) {
+ if (blk->variables[p_identifier]->line > p_context.line) {
+ return false;
}
- }
- }
- GDScriptCompletionIdentifier gdi = _get_native_class(context);
- if (gdi.obj_type != StringName()) {
- bool valid;
- Variant::Type t = ClassDB::get_property_type(gdi.obj_type, p_identifier, &valid);
- if (t != Variant::NIL && valid) {
- r_type.type = t;
- if (t == Variant::INT) {
-//check for enum!
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
+ var_type = blk->variables[p_identifier]->datatype;
- StringName getter = ClassDB::get_property_getter(gdi.obj_type, p_identifier);
- if (getter != StringName()) {
- MethodBind *mb = ClassDB::get_method(gdi.obj_type, getter);
- if (mb) {
- PropertyInfo rt = mb->get_return_info();
- if (rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
- r_type.enumeration = rt.class_name;
- }
- }
+ if (!last_assigned_expression && blk->variables[p_identifier]->assign && blk->variables[p_identifier]->assign->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->variables[p_identifier]->assign);
+ if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN && op->arguments.size() >= 2) {
+ last_assign_line = op->line;
+ last_assigned_expression = op->arguments[1];
}
-#endif
}
- return true;
}
- }
-
- const GDScriptParser::Node *last_assign = NULL;
- int last_assign_line = -1;
-
- for (int i = 0; i < context.block->statements.size(); i++) {
-
- if (context.block->statements[i]->line > p_line)
- continue;
-
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]);
+ for (const List<GDScriptParser::Node *>::Element *E = blk->statements.front(); E; E = E->next()) {
+ const GDScriptParser::Node *expr = E->get();
+ if (expr->line > p_context.line || expr->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ continue;
+ }
- if (lv->assign && lv->name == p_identifier) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(expr);
+ if (op->op != GDScriptParser::OperatorNode::OP_ASSIGN || op->line < last_assign_line) {
+ continue;
+ }
- last_assign = lv->assign;
- last_assign_line = context.block->statements[i]->line;
+ if (op->arguments.size() >= 2 && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
+ if (id->name == p_identifier) {
+ last_assign_line = op->line;
+ last_assigned_expression = op->arguments[1];
+ }
}
}
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(context.block->statements[i]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
-
- if (op->arguments.size() && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
-
- if (id->name == p_identifier) {
-
- last_assign = op->arguments[1];
- last_assign_line = context.block->statements[i]->line;
- }
+ if (blk->if_condition && blk->if_condition->type == GDScriptParser::Node::TYPE_OPERATOR && static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition)->op == GDScriptParser::OperatorNode::OP_IS) {
+ //is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
+ //super dirty hack, but very useful
+ //credit: Zylann
+ //TODO: this could be hacked to detect ANDed conditions too..
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition);
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name == p_identifier) {
+ //bingo
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+ c.block = blk;
+ if (_guess_expression_type(p_context, op->arguments[1], r_type)) {
+ return true;
}
}
}
- }
-
- //use the last assignment, (then backwards?)
- if (last_assign && last_assign_line != p_line) {
- return _guess_expression_type(context, last_assign, last_assign_line, r_type);
+ blk = blk->parent_block;
}
- return false;
-}
-
-static bool _guess_identifier_from_assignment_in_function(GDScriptCompletionContext &context, int p_src_line, const StringName &p_identifier, const StringName &p_function, GDScriptCompletionIdentifier &r_type) {
-
- const GDScriptParser::FunctionNode *func = NULL;
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_function) {
- func = context._class->functions[i];
- break;
+ if (last_assigned_expression && last_assign_line != p_context.line) {
+ GDScriptCompletionContext c = p_context;
+ c.line = last_assign_line;
+ r_type.assigned_expression = last_assigned_expression;
+ if (_guess_expression_type(c, last_assigned_expression, r_type)) {
+ return true;
}
}
- if (!func)
- return false;
+ if (var_type.has_type) {
+ r_type.type = var_type;
+ return true;
+ }
- for (int i = 0; i < func->body->statements.size(); i++) {
+ if (p_context.function) {
+ for (int i = 0; i < p_context.function->arguments.size(); i++) {
+ if (p_context.function->arguments[i] == p_identifier) {
+ if (p_context.function->argument_types[i].has_type) {
+ r_type.type = p_context.function->argument_types[i];
+ return true;
+ }
- if (func->body->statements[i]->line == p_src_line) {
- break;
+ int def_from = p_context.function->arguments.size() - p_context.function->default_values.size();
+ if (i >= def_from) {
+ int def_idx = def_from - i;
+ if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]);
+ if (op->arguments.size() < 2) {
+ return false;
+ }
+ GDScriptCompletionContext c = p_context;
+ c.function = NULL;
+ c.block = NULL;
+ return _guess_expression_type(c, op->arguments[1], r_type);
+ }
+ }
+ break;
+ }
}
- if (func->body->statements[i]->type == GDScriptParser::BlockNode::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(func->body->statements[i]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
-
- if (op->arguments.size() && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
-
- if (id->name == p_identifier) {
-
- return _guess_expression_type(context, op->arguments[1], func->body->statements[i]->line, r_type);
+ GDScriptParser::DataType base_type = p_context._class->base_type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid() && gds->has_method(p_context.function->name)) {
+ GDScriptFunction *func = gds->get_member_functions()[p_context.function->name];
+ if (func) {
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (func->get_argument_name(i) == p_identifier) {
+ r_type = _type_from_gdtype(func->get_argument_type(i));
+ return true;
+ }
+ }
+ }
+ Ref<GDScript> base_gds = gds->get_base_script();
+ if (base_gds.is_valid()) {
+ base_type.kind = GDScriptParser::DataType::GDSCRIPT;
+ base_type.script_type = base_gds;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
}
- }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(base_type.native_type, &methods);
+ ClassDB::get_virtual_methods(base_type.native_type, &methods);
+
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_context.function->name) {
+ MethodInfo &mi = E->get();
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ if (E->get().name == p_identifier) {
+ r_type = _type_from_property(E->get());
+ return true;
+ }
+ }
+ }
+ }
+ base_type.has_type = false;
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
}
- return false;
-}
-
-static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type, bool p_for_indexing) {
-
- //go to block first
+ // Check current class (including inheritance)
+ if (p_context._class) {
+ GDScriptCompletionIdentifier context_base;
+ context_base.value = p_context.base;
+ context_base.type.has_type = true;
+ context_base.type.kind = GDScriptParser::DataType::CLASS;
+ context_base.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ context_base.type.is_meta_type = p_context.function && p_context.function->_static;
- const GDScriptParser::BlockNode *block = context.block;
-
- while (block) {
-
- GDScriptCompletionContext c = context;
- c.block = block;
-
- if (_guess_identifier_type_in_block(c, p_line, p_identifier, r_type)) {
+ if (_guess_identifier_type_from_base(p_context, context_base, p_identifier, r_type)) {
return true;
}
-
- block = block->parent_block;
}
- //guess from argument if virtual
- if (context.function && context.function->name != StringName()) {
-
- int argindex = -1;
+ // Check named scripts
+ if (ScriptServer::is_global_class(p_identifier)) {
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ if (scr.is_valid()) {
+ r_type = _type_from_variant(scr);
+ r_type.type.is_meta_type = true;
+ return true;
+ }
+ return false;
+ }
- for (int i = 0; i < context.function->arguments.size(); i++) {
+ // Check ClassDB
+ if (ClassDB::class_exists(p_identifier)) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = p_identifier;
+ if (Engine::get_singleton()->has_singleton(p_identifier)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ } else {
+ r_type.type.is_meta_type = true;
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
+ r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ }
+ return true;
+ }
- if (context.function->arguments[i] == p_identifier) {
- argindex = i;
- break;
- }
+ // ClassDB again for underscore-prefixed classes
+ StringName under_id = String("_") + p_identifier;
+ if (ClassDB::class_exists(under_id)) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = p_identifier;
+ if (Engine::get_singleton()->has_singleton(p_identifier)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ } else {
+ r_type.type.is_meta_type = true;
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
+ r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
}
+ return true;
+ }
- if (argindex != -1) {
- GDScriptCompletionIdentifier id = _get_native_class(context);
- if (id.type == Variant::OBJECT && id.obj_type != StringName()) {
- //this kinda sucks but meh
+ // Check autoload singletons
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
+ return true;
+ }
- List<MethodInfo> vmethods;
- ClassDB::get_virtual_methods(id.obj_type, &vmethods);
- for (List<MethodInfo>::Element *E = vmethods.front(); E; E = E->next()) {
+ return false;
+}
- if (E->get().name == context.function->name && argindex < E->get().arguments.size()) {
+static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type->constant_expressions.has(p_identifier)) {
+ GDScriptParser::ClassNode::Constant c = base_type.class_type->constant_expressions[p_identifier];
+ r_type.type = c.type;
+ if (c.expression->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ r_type.value = static_cast<const GDScriptParser::ConstantNode *>(c.expression)->value;
+ }
+ return true;
+ }
- PropertyInfo arg = E->get().arguments[argindex];
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->variables.size(); i++) {
+ GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i];
+ if (m.identifier == p_identifier) {
+ if (m.data_type.has_type) {
+ r_type.type = m.data_type;
+ return true;
+ }
+ if (m.expression) {
+ if (_guess_expression_type(p_context, m.expression, r_type)) {
+ return true;
+ }
+ if (m.expression->get_datatype().has_type) {
+ r_type.type = m.expression->get_datatype();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if (gds->get_constants().has(p_identifier)) {
+ r_type = _type_from_variant(gds->get_constants()[p_identifier]);
+ return true;
+ }
+ if (!_static) {
+ const Set<StringName>::Element *m = gds->get_members().find(p_identifier);
+ if (m) {
+ r_type = _type_from_gdtype(gds->get_member_type(p_identifier));
+ return true;
+ }
+ }
+ Ref<GDScript> parent = gds->get_base_script();
+ if (parent.is_valid()) {
+ base_type.script_type = parent;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ if (constants.has(p_identifier)) {
+ r_type = _type_from_variant(constants[p_identifier]);
+ return true;
+ }
- int scp = String(arg.name).find(":");
- if (scp != -1) {
+ if (!_static) {
+ List<PropertyInfo> members;
+ scr->get_script_property_list(&members);
+ for (const List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == p_identifier) {
+ r_type = _type_from_property(prop);
+ return true;
+ }
+ }
+ }
+ Ref<Script> parent = scr->get_base_script();
+ if (parent.is_valid()) {
+ base_type.script_type = parent;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ return false;
+ }
+ }
- r_type.type = Variant::OBJECT;
- r_type.obj_type = String(arg.name).substr(scp + 1, String(arg.name).length());
- return true;
+ // Skip constants since they're all integers. Type does not matter because int has no members
+ List<PropertyInfo> props;
+ ClassDB::get_property_list(class_name, &props);
+ for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == p_identifier) {
+ StringName getter = ClassDB::get_property_getter(class_name, p_identifier);
+ if (getter != StringName()) {
+ MethodBind *g = ClassDB::get_method(class_name, getter);
+ if (g) {
+ r_type = _type_from_property(g->get_return_info());
+ return true;
+ }
} else {
-
- r_type.type = arg.type;
- if (arg.hint == PROPERTY_HINT_RESOURCE_TYPE)
- r_type.obj_type = arg.hint_string;
+ r_type = _type_from_property(prop);
return true;
}
+ break;
}
}
- }
+ return false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ if (err.error != Variant::CallError::CALL_OK) {
+ return false;
+ }
+ bool valid = false;
+ Variant res = tmp.get(p_identifier, &valid);
+ if (valid) {
+ r_type = _type_from_variant(res);
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ return true;
+ }
+ return false;
+ } break;
+ default: {
+ return false;
+ } break;
}
}
- //guess type in constant
+ return false;
+}
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
+static bool _find_last_return_in_block(const GDScriptCompletionContext &p_context, int &r_last_return_line, const GDScriptParser::Node **r_last_returned_value) {
+ if (!p_context.block) {
+ return false;
+ }
- if (context._class->constant_expressions[i].identifier == p_identifier) {
+ for (int i = 0; i < p_context.block->statements.size(); i++) {
+ if (p_context.block->statements[i]->line < r_last_return_line) {
+ continue;
+ }
+ if (p_context.block->statements[i]->type != GDScriptParser::Node::TYPE_CONTROL_FLOW) {
+ continue;
+ }
- ERR_FAIL_COND_V(context._class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT, false);
- r_type = _get_type_from_variant(static_cast<const GDScriptParser::ConstantNode *>(context._class->constant_expressions[i].expression)->value);
- return true;
+ const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(p_context.block->statements[i]);
+ if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_RETURN && cf->arguments.size() > 0) {
+ if (cf->line > r_last_return_line) {
+ r_last_return_line = cf->line;
+ *r_last_returned_value = cf->arguments[0];
+ }
}
}
- if (!(context.function && context.function->_static)) {
+ // Recurse into subblocks
+ for (int i = 0; i < p_context.block->sub_blocks.size(); i++) {
+ GDScriptCompletionContext c = p_context;
+ c.block = p_context.block->sub_blocks[i];
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ }
- for (int i = 0; i < context._class->variables.size(); i++) {
+ return false;
+}
- if (context._class->variables[i].identifier == p_identifier) {
+static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
- if (context._class->variables[i]._export.type != Variant::NIL) {
+ if (_static && p_method == "new") {
+ r_type.type = base_type;
+ r_type.type.is_meta_type = false;
+ r_type.type.is_constant = false;
+ return true;
+ }
- r_type = _get_type_from_pinfo(context._class->variables[i]._export);
- return true;
- } else if (context._class->variables[i].expression) {
- if (p_line <= context._class->variables[i].line)
- return false;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (!base_type.class_type) {
+ base_type.has_type = false;
+ break;
+ }
- bool rtype = _guess_expression_type(context, context._class->variables[i].expression, context._class->variables[i].line, r_type);
- if (rtype && r_type.type != Variant::NIL)
- return true;
- //return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type);
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_method) {
+ int last_return_line = -1;
+ const GDScriptParser::Node *last_returned_value = NULL;
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.function = base_type.class_type->static_functions[i];
+ c.block = c.function->body;
+
+ _find_last_return_in_block(c, last_return_line, &last_returned_value);
+ if (last_returned_value) {
+ c.line = c.block->end_line;
+ return _guess_expression_type(c, last_returned_value, r_type);
+ }
+ }
+ }
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_method) {
+ int last_return_line = -1;
+ const GDScriptParser::Node *last_returned_value = NULL;
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.function = base_type.class_type->functions[i];
+ c.block = c.function->body;
+
+ _find_last_return_in_block(c, last_return_line, &last_returned_value);
+ if (last_returned_value) {
+ c.line = c.block->end_line;
+ return _guess_expression_type(c, last_returned_value, r_type);
+ }
+ }
+ }
}
- //try to guess from assignment in constructor or _ready
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_ready", r_type))
- return true;
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_enter_tree", r_type))
- return true;
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_init", r_type))
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if (gds->get_member_functions().has(p_method)) {
+ r_type = _type_from_gdtype(gds->get_member_functions()[p_method]->get_return_type());
+ return true;
+ }
+ Ref<GDScript> base_script = gds->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ List<MethodInfo> methods;
+ scr->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+ if (mi.name == p_method) {
+ r_type = _type_from_property(mi.return_val);
+ return true;
+ }
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName native = base_type.native_type;
+ if (!ClassDB::class_exists(native)) {
+ native = String("_") + native;
+ if (!ClassDB::class_exists(native)) {
+ return false;
+ }
+ }
+ MethodBind *mb = ClassDB::get_method(native, p_method);
+ if (mb) {
+ r_type = _type_from_property(mb->get_return_info());
return true;
+ }
+ return false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return false;
+ }
+
+ List<MethodInfo> methods;
+ tmp.get_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+ if (mi.name == p_method) {
+ r_type = _type_from_property(mi.return_val);
+ return true;
+ }
+ }
+ return false;
+ } break;
+ default: {
return false;
}
}
}
+ return false;
+}
- //autoloads as singletons
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
- String s = E->get().name;
- if (!s.begins_with("autoload/"))
- continue;
- String name = s.get_slice("/", 1);
- if (name == String(p_identifier)) {
+static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx) {
- String path = ProjectSettings::get_singleton()->get(s);
- if (path.begins_with("*")) {
- String script = path.substr(1, path.length());
+ String arghint = _get_visual_datatype(p_info.return_val, false) + " " + p_info.name + "(";
- if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
- script = script.get_basename() + ".gd";
- }
+ int def_args = p_info.arguments.size() - p_info.default_arguments.size();
+ int i = 0;
+ for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
+ if (i > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
+ }
- if (FileAccess::exists(script)) {
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += _get_visual_datatype(E->get(), true) + " " + E->get().name;
- //print_line("is a script");
+ if (i - def_args >= 0) {
+ arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
+ }
- Ref<Script> scr;
- if (ScriptCodeCompletionCache::get_singleton())
- scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
- else
- scr = ResourceLoader::load(script);
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
- r_type.obj_type = "Node";
- r_type.type = Variant::OBJECT;
- r_type.script = scr;
- r_type.value = Variant();
+ i++;
+ }
- return true;
- }
- }
+ if (p_info.flags & METHOD_FLAG_VARARG) {
+ if (p_info.arguments.size() > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
}
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += "...";
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
+ }
+ }
+ if (p_info.arguments.size() > 0 || (p_info.flags & METHOD_FLAG_VARARG)) {
+ arghint += " ";
}
- //global
- for (Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
- if (E->key() == p_identifier) {
+ arghint += ")";
- r_type = _get_type_from_variant(GDScriptLanguage::get_singleton()->get_global_array()[E->get()], !p_for_indexing);
- return true;
- }
- }
- return false;
+ return arghint;
}
-static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p_line, bool p_only_functions, Set<String> &result) {
+static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
- if (p_only_functions)
- return;
+ String arghint = p_function->return_type.to_string() + " " + p_function->name.operator String() + "(";
- for (int i = 0; i < context.block->statements.size(); i++) {
+ int def_args = p_function->arguments.size() - p_function->default_values.size();
+ for (int i = 0; i < p_function->arguments.size(); i++) {
+ if (i > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
+ }
- GDScriptParser::Node *statement = context.block->statements[i];
- if (statement->line > p_line)
- continue;
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += p_function->argument_types[i].to_string() + " " + p_function->arguments[i].operator String();
+
+ if (i - def_args >= 0) {
+ String def_val = "<unknown>";
+ if (p_function->default_values[i - def_args] && p_function->default_values[i - def_args]->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *assign = static_cast<const GDScriptParser::OperatorNode *>(p_function->default_values[i - def_args]);
+
+ if (assign->arguments.size() >= 2) {
+ if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(assign->arguments[1]);
+ def_val = cn->value.get_construct_string();
+ } else if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->arguments[1]);
+ def_val = id->name.operator String();
+ }
+ }
+ }
+ arghint += " = " + def_val;
+ }
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ }
- GDScriptParser::BlockNode::Type statementType = statement->type;
- if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
+ if (p_function->arguments.size() > 0) {
+ arghint += " ";
+ }
+ arghint += ")";
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement);
- result.insert(lv->name.operator String());
- } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) {
+ return arghint;
+}
- const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
- if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) {
+static void _find_enumeration_candidates(const String p_enum_hint, Set<String> &r_result) {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]);
- result.insert(id->name.operator String());
+ if (p_enum_hint.find(".") == -1) {
+ // Global constant
+ StringName current_enum = p_enum_hint;
+ for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
+ if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
+ r_result.insert(GlobalConstants::get_global_constant_name(i));
}
}
- }
-}
-
-static void _find_identifiers_in_class(GDScriptCompletionContext &context, bool p_static, bool p_only_functions, Set<String> &result) {
+ } else {
+ String class_name = p_enum_hint.get_slice(".", 0);
+ String enum_name = p_enum_hint.get_slice(".", 1);
- if (!p_static && !p_only_functions) {
+ if (!ClassDB::class_exists(class_name)) {
+ return;
+ }
- for (int i = 0; i < context._class->variables.size(); i++) {
- result.insert(context._class->variables[i].identifier);
+ List<StringName> enum_constants;
+ ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);
+ for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
+ String candidate = class_name + "." + E->get();
+ r_result.insert(candidate);
}
}
- if (!p_only_functions) {
+}
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
- result.insert(context._class->constant_expressions[i].identifier);
+static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Set<String> &r_result) {
+ for (Map<StringName, GDScriptParser::LocalVarNode *>::Element *E = p_context.block->variables.front(); E; E = E->next()) {
+ if (E->get()->line < p_context.line) {
+ r_result.insert(E->key().operator String());
}
+ }
+ if (p_context.block->parent_block) {
+ GDScriptCompletionContext c = p_context;
+ c.block = p_context.block->parent_block;
+ _find_identifiers_in_block(c, r_result);
+ }
+}
+
+static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result);
- for (int i = 0; i < context._class->subclasses.size(); i++) {
- result.insert(context._class->subclasses[i]->name);
+static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Set<String> &r_result) {
+ if (!p_parent_only) {
+ if (!p_static && !p_only_functions) {
+ for (int i = 0; i < p_context._class->variables.size(); i++) {
+ r_result.insert(p_context._class->variables[i].identifier);
+ }
}
- }
- for (int i = 0; i < context._class->static_functions.size(); i++) {
- if (context._class->static_functions[i]->arguments.size())
- result.insert(context._class->static_functions[i]->name.operator String() + "(");
- else
- result.insert(context._class->static_functions[i]->name.operator String() + "()");
- }
+ if (!p_only_functions) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_context._class->constant_expressions.front(); E; E = E->next()) {
+ r_result.insert(E->key());
+ }
+ for (int i = 0; i < p_context._class->subclasses.size(); i++) {
+ r_result.insert(p_context._class->subclasses[i]->name);
+ }
+ }
- if (!p_static) {
+ for (int i = 0; i < p_context._class->static_functions.size(); i++) {
+ if (p_context._class->static_functions[i]->arguments.size()) {
+ r_result.insert(p_context._class->static_functions[i]->name.operator String() + "(");
+ } else {
+ r_result.insert(p_context._class->static_functions[i]->name.operator String() + "()");
+ }
+ }
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->arguments.size())
- result.insert(context._class->functions[i]->name.operator String() + "(");
- else
- result.insert(context._class->functions[i]->name.operator String() + "()");
+ if (!p_static) {
+ for (int i = 0; i < p_context._class->functions.size(); i++) {
+ if (p_context._class->functions[i]->arguments.size()) {
+ r_result.insert(p_context._class->functions[i]->name.operator String() + "(");
+ } else {
+ r_result.insert(p_context._class->functions[i]->name.operator String() + "()");
+ }
+ }
}
}
- //globals
-
- Ref<Reference> base = _get_parent_class(context);
+ // Parents
+ GDScriptCompletionIdentifier base_type;
+ base_type.type = p_context._class->base_type;
+ base_type.type.is_meta_type = p_static;
+ base_type.value = p_context.base;
- while (true) {
+ GDScriptCompletionContext c = p_context;
+ c.block = NULL;
+ c.function = NULL;
- Ref<GDScript> script = base;
- Ref<GDScriptNativeClass> nc = base;
- if (script.is_valid()) {
+ _find_identifiers_in_base(c, base_type, p_only_functions, r_result);
+}
- if (!p_static && !p_only_functions) {
- for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) {
- result.insert(E->get().operator String());
+static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
+
+ if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ r_result.insert("new(");
+ }
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.block = NULL;
+ c.function = NULL;
+ _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> script = base_type.script_type;
+ if (script.is_valid()) {
+ if (!_static && !p_only_functions) {
+ for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) {
+ r_result.insert(E->get().operator String());
+ }
+ }
+ if (!p_only_functions) {
+ for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
+ for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
+ if (!_static || E->get()->is_static()) {
+ if (E->get()->get_argument_count()) {
+ r_result.insert(E->key().operator String() + "(");
+ } else {
+ r_result.insert(E->key().operator String() + "()");
+ }
+ }
+ }
+ if (!p_only_functions) {
+ for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
+ base_type = GDScriptParser::DataType();
+ if (script->get_base().is_valid()) {
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::GDSCRIPT;
+ base_type.script_type = script->get_base();
+ } else {
+ base_type.has_type = script->get_instance_base_type() != StringName();
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.script_type = script->get_instance_base_type();
+ }
+ } else {
+ return;
}
- }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ if (!_static && !p_only_functions) {
+ List<PropertyInfo> members;
+ scr->get_script_property_list(&members);
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ r_result.insert(E->get().name);
+ }
+ }
+ if (!p_only_functions) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
- if (!p_only_functions) {
- for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
- }
- }
+ List<MethodInfo> methods;
+ scr->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
+ }
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
- if (!p_static || E->get()->is_static()) {
- if (E->get()->get_argument_count())
- result.insert(E->key().operator String() + "(");
- else
- result.insert(E->key().operator String() + "()");
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return;
}
- }
-
- if (!p_only_functions) {
- for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName type = base_type.native_type;
+ if (!ClassDB::class_exists(type)) {
+ type = String("_") + type;
+ if (!ClassDB::class_exists(type)) {
+ return;
+ }
}
- }
- base = script->get_base();
- if (base.is_null())
- base = script->get_native();
- } else if (nc.is_valid()) {
+ if (!p_only_functions) {
+ List<String> constants;
+ ClassDB::get_integer_constant_list(type, &constants);
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ r_result.insert(E->get());
+ }
- StringName type = nc->get_name();
+ if (!_static) {
+ List<PropertyInfo> pinfo;
+ ClassDB::get_property_list(type, &pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ continue;
+ }
+ if (E->get().name.find("/") != -1) {
+ continue;
+ }
+ r_result.insert(E->get().name);
+ }
+ }
+ }
- if (!p_only_functions) {
+ if (!_static) {
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(type, &methods, false, true);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("_")) {
+ continue;
+ }
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
+ }
+ }
- List<String> constants;
- ClassDB::get_integer_constant_list(type, &constants);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- result.insert(E->get());
+ return;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return;
}
- List<PropertyInfo> pinfo;
+ if (!p_only_functions) {
+ List<PropertyInfo> members;
+ tmp.get_property_list(&members);
- ClassDB::get_property_list(type, &pinfo);
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ if (String(E->get().name).find("/") == -1) {
+ r_result.insert(E->get().name);
+ }
+ }
+ }
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- result.insert(E->get().name);
+ List<MethodInfo> methods;
+ tmp.get_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
}
- }
- List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods, false, true);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_"))
- continue;
- if (E->get().arguments.size())
- result.insert(E->get().name + "(");
- else
- result.insert(E->get().name + "()");
- }
- break;
- } else
- break;
+ return;
+ } break;
+ default: {
+ return;
+ } break;
+ }
}
}
-static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bool p_only_functions, Set<String> &result) {
+static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Set<String> &r_result) {
- const GDScriptParser::BlockNode *block = context.block;
+ const GDScriptParser::BlockNode *block = p_context.block;
- if (context.function) {
+ if (p_context.function) {
- const GDScriptParser::FunctionNode *f = context.function;
+ const GDScriptParser::FunctionNode *f = p_context.function;
for (int i = 0; i < f->arguments.size(); i++) {
- result.insert(f->arguments[i].operator String());
+ r_result.insert(f->arguments[i].operator String());
}
}
- while (block) {
-
- GDScriptCompletionContext c = context;
+ if (!p_only_functions && block) {
+ GDScriptCompletionContext c = p_context;
c.block = block;
-
- _find_identifiers_in_block(c, p_line, p_only_functions, result);
- block = block->parent_block;
+ _find_identifiers_in_block(c, r_result);
}
- const GDScriptParser::ClassNode *clss = context._class;
-
- bool _static = context.function && context.function->_static;
+ const GDScriptParser::ClassNode *clss = p_context._class;
+ bool _static = !p_context.function || p_context.function->_static;
while (clss) {
- GDScriptCompletionContext c = context;
+ GDScriptCompletionContext c = p_context;
c._class = clss;
c.block = NULL;
c.function = NULL;
- _find_identifiers_in_class(c, _static, p_only_functions, result);
+ _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
+ _static = true;
clss = clss->owner;
}
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-
- result.insert(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+ MethodInfo mi = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
+ if (mi.arguments.size() || (mi.flags & METHOD_FLAG_VARARG)) {
+ r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "(");
+ } else {
+ r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "()");
+ }
}
static const char *_type_names[Variant::VARIANT_MAX] = {
@@ -1526,667 +2091,372 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo
};
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- result.insert(_type_names[i]);
+ r_result.insert(_type_names[i]);
}
- List<String> reserved_words;
- GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words);
+ static const char *_keywords[] = {
+ "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
+ "breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
+ "const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif",
+ "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "slave",
+ "remotesync", "mastersync", "slavesync",
+ 0
+ };
- for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) {
- result.insert(E->get());
+ const char **kw = _keywords;
+ while (*kw) {
+ r_result.insert(*kw);
+ kw++;
}
- //autoload singletons
+ // Autoload singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
-
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
String s = E->get().name;
- if (!s.begins_with("autoload/"))
+ if (!s.begins_with("autoload/")) {
continue;
- String name = s.get_slice("/", 1);
+ }
String path = ProjectSettings::get_singleton()->get(s);
if (path.begins_with("*")) {
- result.insert(name);
+ r_result.insert(s.get_slice("/", 1));
}
}
- for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
- }
-}
-
-static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = true) {
-
- String n = p_info.name;
- int idx = n.find(":");
- if (idx != -1) {
- return n.substr(idx + 1, n.length());
+ // Named scripts
+ List<StringName> named_scripts;
+ ScriptServer::get_global_class_list(&named_scripts);
+ for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
+ r_result.insert(E->get().operator String());
}
- if (p_info.type == Variant::OBJECT && p_info.hint == PROPERTY_HINT_RESOURCE_TYPE)
- return p_info.hint_string;
- if (p_info.type == Variant::NIL) {
- if (p_isarg)
- return "var";
- else
- return "void";
+ // Native classes
+ for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
}
-
- return Variant::get_type_name(p_info.type);
}
-static void _make_function_hint(const GDScriptParser::FunctionNode *p_func, int p_argidx, String &arghint) {
-
- arghint = "func " + p_func->name + "(";
- for (int i = 0; i < p_func->arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += p_func->arguments[i].operator String();
- int deffrom = p_func->arguments.size() - p_func->default_values.size();
-
- if (i >= deffrom) {
- int defidx = deffrom - i;
-
- if (defidx >= 0 && defidx < p_func->default_values.size()) {
-
- if (p_func->default_values[defidx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
-
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[defidx]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]);
- arghint += "=" + cn->value.get_construct_string();
+static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Set<String> &r_result, String &r_arghint) {
+ Variant base = p_base.value;
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = false;
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_method) {
+ r_arghint = _make_arguments_hint(base_type.class_type->static_functions[i], p_argidx);
+ return;
+ }
+ }
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_method) {
+ r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx);
+ return;
+ }
}
- } else {
}
- }
- }
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (p_func->arguments.size() > 0)
- arghint += " ";
- arghint += ")";
-}
-
-void get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) {
-
- for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- get_directory_contents(p_dir->get_subdir(i), r_list);
- }
-
- for (int i = 0; i < p_dir->get_file_count(); i++) {
- r_list.insert("\"" + p_dir->get_file_path(i) + "\"");
- }
-}
-
-static void _find_type_arguments(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, const StringName &p_method, const GDScriptCompletionIdentifier &id, int p_argidx, Set<String> &result, bool &r_forced, String &arghint) {
-
- //print_line("find type arguments?");
- if (id.type == Variant::OBJECT && id.obj_type != StringName()) {
-
- MethodBind *m = ClassDB::get_method(id.obj_type, p_method);
- if (!m) {
- //not in static method, see script
-
- //print_line("not in static: "+String(p_method));
- Ref<GDScript> on_script;
-
- if (id.value.get_type()) {
- Object *obj = id.value;
- GDScript *scr = Object::cast_to<GDScript>(obj);
- if (scr) {
- while (scr) {
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
+ r_result.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\"");
+ }
+ }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->is_static() && p_method == E->get()->get_name()) {
- arghint = "static func " + String(p_method) + "(";
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- arghint += ")";
- return; //found
- }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ List<MethodInfo> signals;
+ gds->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().name + "\"");
}
-
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ }
+ Ref<GDScript> base_script = gds->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
}
} else {
- if (obj) {
- on_script = obj->get_script();
+ return;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ base_type.has_type = false;
+ break;
}
}
- }
-
- //print_line("but it has a script?");
- if (!on_script.is_valid() && id.script.is_valid()) {
- //print_line("yes");
- on_script = id.script;
- }
-
- if (on_script.is_valid()) {
-
- GDScript *scr = on_script.ptr();
- if (scr) {
- while (scr) {
-
- String code = scr->get_source_code();
- //print_line("has source code!");
-
- if (code != "") {
- //if there is code, parse it. This way is slower but updates in real-time
- GDScriptParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
-
- Error err = p.parse(scr->get_source_code(), scr->get_path().get_base_dir(), true, "", false);
-
- if (err == OK) {
- //print_line("checking the functions...");
- //only if ok, otherwise use what is cached on the script
- //GDScriptParser::ClassNode *base = p.
- const GDScriptParser::Node *root = p.get_parse_tree();
- ERR_FAIL_COND(root->type != GDScriptParser::Node::TYPE_CLASS);
-
- const GDScriptParser::ClassNode *cl = static_cast<const GDScriptParser::ClassNode *>(root);
-
- const GDScriptParser::FunctionNode *func = NULL;
- bool st = false;
-
- for (int i = 0; i < cl->functions.size(); i++) {
- //print_line(String(cl->functions[i]->name)+" vs "+String(p_method));
- if (cl->functions[i]->name == p_method) {
- func = cl->functions[i];
- }
- }
-
- for (int i = 0; i < cl->static_functions.size(); i++) {
-
- //print_line(String(cl->static_functions[i]->name)+" vs "+String(p_method));
- if (cl->static_functions[i]->name == p_method) {
- func = cl->static_functions[i];
- st = true;
- }
- }
-
- if (func) {
-
- arghint = "func " + String(p_method) + "(";
- if (st)
- arghint = "static " + arghint;
- for (int i = 0; i < func->arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + String(func->arguments[i]);
- int deffrom = func->arguments.size() - func->default_values.size();
- if (i >= deffrom) {
- int defidx = deffrom - i;
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(class_name, &methods);
+ ClassDB::get_virtual_methods(class_name, &methods);
+ int method_args = 0;
- if (defidx >= 0 && defidx < func->default_values.size() && func->default_values[defidx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(func->default_values[defidx]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]);
- arghint += "=" + cn->value.get_construct_string();
- }
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
-
- arghint += " )";
- return;
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_method) {
+ method_args = E->get().arguments.size();
+ if (base.get_type() == Variant::OBJECT) {
+ Object *obj = base.operator Object *();
+ if (obj) {
+ List<String> options;
+ obj->get_argument_options(p_method, p_argidx, &options);
+ for (List<String>::Element *E = options.front(); E; E = E->next()) {
+ r_result.insert(E->get());
}
- } else {
- //print_line("failed parsing?");
- code = "";
}
}
- if (code == "") {
-
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (p_method == E->get()->get_name()) {
- arghint = "func " + String(p_method) + "(";
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- arghint += ")";
- return; //found
- }
+ if (p_argidx < method_args) {
+ PropertyInfo arg_info = E->get().arguments[p_argidx];
+ if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ _find_enumeration_candidates(arg_info.class_name, r_result);
}
}
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ break;
}
}
- }
-
- } else {
-
- //regular method
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
- if (p_argidx < m->get_argument_count()) {
- PropertyInfo pi = m->get_argument_info(p_argidx);
-
- if (pi.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
- String enumeration = pi.class_name;
- if (enumeration.find(".") != -1) {
- //class constant
- List<StringName> constants;
- String cls = enumeration.get_slice(".", 0);
- String enm = enumeration.get_slice(".", 1);
-
- ClassDB::get_enum_constants(cls, enm, &constants);
- //constants.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- String add = cls + "." + E->get();
- result.insert(add);
- r_forced = true;
- }
- } else {
- //global constant
- StringName current_enum = enumeration;
-
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- result.insert(GlobalConstants::get_global_constant_name(i));
- r_forced = true;
- }
- }
- //global
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(class_name, &signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().name + "\"");
}
}
- }
-#endif
- if (p_method.operator String() == "connect" || (p_method.operator String() == "emit_signal" && p_argidx == 0)) {
-
- if (p_argidx == 0) {
- List<MethodInfo> sigs;
- ClassDB::get_signal_list(id.obj_type, &sigs);
-
- if (id.script.is_valid()) {
- id.script->get_script_signal_list(&sigs);
- } else if (id.value.get_type() == Variant::OBJECT) {
- Object *obj = id.value;
- if (obj && !obj->get_script().is_null()) {
- Ref<Script> scr = obj->get_script();
- if (scr.is_valid()) {
- scr->get_script_signal_list(&sigs);
- }
- }
- }
-
- for (List<MethodInfo>::Element *E = sigs.front(); E; E = E->next()) {
- result.insert("\"" + E->get().name + "\"");
- r_forced = true;
- }
- } else if (p_argidx == 2) {
+ if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
+ // Get autoloads
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
- if (context._class) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- result.insert("\"" + context._class->functions[i]->name + "\"");
- r_forced = true;
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
}
+ String name = s.get_slice("/", 1);
+ r_result.insert("\"/root/" + name + "\"");
}
}
- /*if (p_argidx==2) {
-
- ERR_FAIL_COND(p_node->type!=GDScriptParser::Node::TYPE_OPERATOR);
- const GDScriptParser::OperatorNode *op=static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->arguments.size()>)
-
- }*/
- } else {
-
- if (p_argidx == 0 && (String(p_method) == "get_node" || String(p_method) == "has_node") && ClassDB::is_parent_class(id.obj_type, "Node")) {
+ if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) {
+ // Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
-
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
String s = E->get().name;
- if (!s.begins_with("autoload/"))
+ if (!s.begins_with("input/")) {
continue;
- //print_line("found "+s);
+ }
String name = s.get_slice("/", 1);
- result.insert("\"/root/" + name + "\"");
- r_forced = true;
+ r_result.insert("\"" + name + "\"");
}
}
- Object *obj = id.value;
- if (obj) {
- List<String> options;
- obj->get_argument_options(p_method, p_argidx, &options);
-
- for (List<String>::Element *E = options.front(); E; E = E->next()) {
-
- result.insert(E->get());
- r_forced = true;
+ base_type.has_type = false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ if (base.get_type() == Variant::NIL) {
+ Variant::CallError err;
+ base = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return;
}
}
- }
-
- arghint = _get_visual_datatype(m->get_return_info(), false) + " " + p_method.operator String() + String("(");
-
- for (int i = 0; i < m->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- String n = m->get_argument_info(i).name;
- int dp = n.find(":");
- if (dp != -1)
- n = n.substr(0, dp);
- arghint += _get_visual_datatype(m->get_argument_info(i)) + " " + n;
- int deffrom = m->get_argument_count() - m->get_default_argument_count();
-
- if (i >= deffrom) {
- int defidx = i - deffrom;
- if (defidx >= 0 && defidx < m->get_default_argument_count()) {
- Variant v = m->get_default_argument(i);
- arghint += "=" + v.get_construct_string();
+ List<MethodInfo> methods;
+ base.get_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_method) {
+ r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ return;
}
}
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (m->get_argument_count() > 0)
- arghint += " ";
-
- arghint += ")";
+ base_type.has_type = false;
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
}
-static void _find_call_arguments(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, int p_argidx, Set<String> &result, bool &r_forced, String &arghint) {
+static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) {
if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) {
-
return;
}
+ Variant base;
+ GDScriptParser::DataType base_type;
+ StringName function;
+ bool _static = false;
const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->op != GDScriptParser::OperatorNode::OP_CALL) {
+ GDScriptCompletionIdentifier connect_base;
+ if (op->op != GDScriptParser::OperatorNode::OP_CALL && op->op != GDScriptParser::OperatorNode::OP_PARENT_CALL) {
return;
}
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- //complete built-in function
- const GDScriptParser::BuiltInFunctionNode *fn = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- MethodInfo mi = GDScriptFunctions::get_info(fn->function);
+ if (!op->arguments.size()) {
+ return;
+ }
- if (mi.name == "load" && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), result);
- }
+ if (op->op == GDScriptParser::OperatorNode::OP_CALL) {
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
+ // Complete built-in function
+ const GDScriptParser::BuiltInFunctionNode *fn = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(fn->function);
- arghint = _get_visual_datatype(mi.return_val, false) + " " + GDScriptFunctions::get_func_name(fn->function) + String("(");
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) {
- arghint += String::chr(0xFFFF);
- }
- arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name;
- if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) {
- arghint += String::chr(0xFFFF);
+ if ((mi.name == "load" || mi.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
}
- }
- if (mi.arguments.size() > 0)
- arghint += " ";
- arghint += ")";
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- //complete constructor
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
+ r_arghint = _make_arguments_hint(mi, p_argidx);
+ return;
- List<MethodInfo> mil;
- Variant::get_constructor_list(tn->vtype, &mil);
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
+ // Complete constructor
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
- for (List<MethodInfo>::Element *E = mil.front(); E; E = E->next()) {
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(tn->vtype, &constructors);
- MethodInfo mi = E->get();
- if (mi.arguments.size() == 0)
- continue;
- if (E->prev())
- arghint += "\n";
- arghint += Variant::get_type_name(tn->vtype) + " " + Variant::get_type_name(tn->vtype) + String("(");
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name;
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
+ int i = 0;
+ for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
+ if (p_argidx >= E->get().arguments.size()) {
+ continue;
}
- }
- if (mi.arguments.size() > 0)
- arghint += " ";
- arghint += ")";
- }
-
- } else if (op->arguments.size() >= 2 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- //make sure identifier exists...
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_SELF) {
- //self, look up
-
- for (int i = 0; i < context._class->static_functions.size(); i++) {
- if (context._class->static_functions[i]->name == id->name) {
- _make_function_hint(context._class->static_functions[i], p_argidx, arghint);
- return;
+ if (i > 0) {
+ r_arghint += "\n";
}
+ r_arghint += _make_arguments_hint(E->get(), p_argidx);
+ i++;
}
+ return;
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_SELF) {
- if (context.function && !context.function->_static) {
-
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == id->name) {
- _make_function_hint(context._class->functions[i], p_argidx, arghint);
- return;
- }
- }
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
}
- Ref<Reference> base = _get_parent_class(context);
-
- while (true) {
-
- Ref<GDScript> script = base;
- Ref<GDScriptNativeClass> nc = base;
- if (script.is_valid()) {
-
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
+ base = p_context.base;
- if (E->key() == id->name) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
+ function = id->name;
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ _static = p_context.function && p_context.function->_static;
- if (context.function && context.function->_static && !E->get()->is_static())
- continue;
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
- arghint = "func " + id->name.operator String() + String("(");
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (E->get()->get_argument_count() > 0)
- arghint += " ";
- arghint += ")";
- return;
- }
- }
+ } else {
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
+ }
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
+ function = id->name;
- base = script->get_base();
- if (base.is_null())
- base = script->get_native();
- } else if (nc.is_valid()) {
+ GDScriptCompletionIdentifier ci;
+ if (_guess_expression_type(p_context, op->arguments[0], ci)) {
+ base_type = ci.type;
+ base = ci.value;
+ } else {
+ return;
+ }
+ _static = ci.type.is_meta_type;
- if (!(context.function && context.function->_static)) {
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
+ }
+ } else {
+ if (!p_context._class || op->arguments.size() < 1 || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
+ }
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ base_type.is_meta_type = p_context.function && p_context.function->_static;
+ base = p_context.base;
- GDScriptCompletionIdentifier ci;
- ci.type = Variant::OBJECT;
- ci.obj_type = nc->get_name();
- if (!context._class->owner)
- ci.value = context.base;
+ function = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
- _find_type_arguments(context, p_node, p_line, id->name, ci, p_argidx, result, r_forced, arghint);
- //guess type..
- /*
- List<MethodInfo> methods;
- ClassDB::get_method_list(type,&methods);
- for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
- if (E->get().arguments.size())
- result.insert(E->get().name+"(");
- else
- result.insert(E->get().name+"()");
- }*/
- }
- break;
- } else
- break;
- }
- } else {
- //indexed lookup
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
+ }
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, op->arguments[0], p_line, ci)) {
+ GDScriptCompletionIdentifier ci;
+ ci.type = base_type;
+ ci.value = base;
+ _find_call_arguments(p_context, ci, function, p_argidx, _static, r_result, r_arghint);
- _find_type_arguments(context, p_node, p_line, id->name, ci, p_argidx, result, r_forced, arghint);
- return;
- }
+ if (function == "connect" && p_argidx == 2) {
+ Set<String> methods;
+ _find_identifiers_in_base(p_context, connect_base, true, methods);
+ for (Set<String>::Element *E = methods.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().replace("(", "").replace(")", "") + "\"");
}
}
+
+ r_forced = r_result.size() > 0;
}
Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
- GDScriptParser p;
+ GDScriptParser parser;
- p.parse(p_code, p_base_path, false, "", true);
- bool isfunction = false;
- Set<String> options;
+ parser.parse(p_code, p_base_path, false, "", true);
r_forced = false;
+ Set<String> options;
GDScriptCompletionContext context;
- context._class = p.get_completion_class();
- context.block = p.get_completion_block();
- context.function = p.get_completion_function();
+ context._class = parser.get_completion_class();
+ context.block = parser.get_completion_block();
+ context.function = parser.get_completion_function();
context.base = p_owner;
context.base_path = p_base_path;
+ context.line = parser.get_completion_line();
+ bool is_function = false;
- switch (p.get_completion_type()) {
-
+ switch (parser.get_completion_type()) {
case GDScriptParser::COMPLETION_NONE: {
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
- Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(), &constants);
+ Variant::get_numeric_constants_for_type(parser.get_completion_built_in_constant(), &constants);
for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
options.insert(E->get().operator String());
}
-
- } break;
- case GDScriptParser::COMPLETION_FUNCTION:
- isfunction = true;
- case GDScriptParser::COMPLETION_IDENTIFIER: {
-
- _find_identifiers(context, p.get_completion_line(), isfunction, options);
} break;
case GDScriptParser::COMPLETION_PARENT_FUNCTION: {
-
+ _find_identifiers_in_class(context, !context.function || context.function->_static, true, true, options);
+ } break;
+ case GDScriptParser::COMPLETION_FUNCTION: {
+ is_function = true;
+ } // fallthrough
+ case GDScriptParser::COMPLETION_IDENTIFIER: {
+ _find_identifiers(context, is_function, options);
} break;
case GDScriptParser::COMPLETION_GET_NODE: {
-
if (p_owner) {
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
@@ -2204,315 +2474,358 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
}
}
+
+ // Get autoloads
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ options.insert("\"/root/" + name + "\"");
+ }
}
} break;
- case GDScriptParser::COMPLETION_METHOD:
- isfunction = true;
+ case GDScriptParser::COMPLETION_METHOD: {
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_INDEX: {
-
- const GDScriptParser::Node *node = p.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR)
+ const GDScriptParser::Node *node = parser.get_completion_node();
+ if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
break;
+ }
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(node);
+ if (op->arguments.size() < 1) {
+ break;
+ }
- GDScriptCompletionIdentifier t;
- if (_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], p.get_completion_line(), t, true)) {
-
- if (t.type == Variant::OBJECT && t.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = t.value;
- if (gdn.is_valid()) {
- StringName cn = gdn->get_name();
- List<String> cnames;
- ClassDB::get_integer_constant_list(cn, &cnames);
- for (List<String>::Element *E = cnames.front(); E; E = E->next()) {
- options.insert(E->get());
- }
-
- List<PropertyInfo> pinfo;
- ClassDB::get_property_list(cn, &pinfo);
-
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- options.insert(E->get().name);
- }
- }
- } else if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
-
- Ref<GDScript> on_script;
-
- if (t.value.get_type()) {
- Object *obj = t.value;
-
- GDScript *scr = Object::cast_to<GDScript>(obj);
- if (scr) {
- while (scr) {
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(context, op->arguments[0], base)) {
+ break;
+ }
- if (!isfunction) {
- for (const Map<StringName, Variant>::Element *E = scr->get_constants().front(); E; E = E->next()) {
- options.insert(E->key());
- }
- }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->is_static())
- options.insert(E->key());
- }
+ GDScriptCompletionContext c = context;
+ c.function = NULL;
+ c.block = NULL;
+ c.base = base.value.get_type() == Variant::OBJECT ? base.value.operator Object *() : NULL;
+ if (base.type.kind == GDScriptParser::DataType::CLASS) {
+ c._class = base.type.class_type;
+ } else {
+ c._class = NULL;
+ }
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ _find_identifiers_in_base(c, base, is_function, options);
+ } break;
+ case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
+ _find_call_arguments(context, parser.get_completion_node(), parser.get_completion_argument_index(), options, r_forced, r_call_hint);
+ } break;
+ case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
+ GDScriptParser::DataType native_type = context._class->base_type;
+ while (native_type.has_type && native_type.kind != GDScriptParser::DataType::NATIVE) {
+ switch (native_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ native_type = native_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = native_type.script_type;
+ if (gds.is_valid()) {
+ Ref<GDScript> base = gds->get_base_script();
+ if (base.is_valid()) {
+ native_type.script_type = base;
+ } else {
+ native_type.native_type = gds->get_instance_base_type();
+ native_type.kind = GDScriptParser::DataType::NATIVE;
}
} else {
- if (obj) {
- on_script = obj->get_script();
- }
+ native_type.has_type = false;
}
- }
-
- if (!on_script.is_valid() && t.script.is_valid()) {
- on_script = t.script;
- }
-
- if (on_script.is_valid()) {
-
- GDScript *scr = on_script.ptr();
- if (scr) {
- while (scr) {
-
- String code = scr->get_source_code();
-
- if (code != "") {
- //if there is code, parse it. This way is slower but updates in real-time
- GDScriptParser p;
-
- Error err = p.parse(scr->get_source_code(), scr->get_path().get_base_dir(), true, "", false);
-
- if (err == OK) {
- //only if ok, otherwise use what is cached on the script
- //GDScriptParser::ClassNode *base = p.
- const GDScriptParser::Node *root = p.get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_PARSE_ERROR);
-
- const GDScriptParser::ClassNode *cl = static_cast<const GDScriptParser::ClassNode *>(root);
-
- for (int i = 0; i < cl->functions.size(); i++) {
-
- if (cl->functions[i]->arguments.size())
- options.insert(String(cl->functions[i]->name) + "(");
- else
- options.insert(String(cl->functions[i]->name) + "()");
- }
-
- for (int i = 0; i < cl->static_functions.size(); i++) {
-
- if (cl->static_functions[i]->arguments.size())
- options.insert(String(cl->static_functions[i]->name) + "(");
- else
- options.insert(String(cl->static_functions[i]->name) + "()");
- }
+ } break;
+ default: {
+ native_type.has_type = false;
+ } break;
+ }
+ }
- if (!isfunction) {
- for (int i = 0; i < cl->variables.size(); i++) {
+ if (!native_type.has_type) {
+ break;
+ }
- options.insert(String(cl->variables[i].identifier));
- }
+ StringName class_name = native_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ break;
+ }
+ }
- for (int i = 0; i < cl->constant_expressions.size(); i++) {
+ bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool();
- options.insert(String(cl->constant_expressions[i].identifier));
- }
- }
+ List<MethodInfo> virtual_methods;
+ ClassDB::get_virtual_methods(class_name, &virtual_methods);
+ for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- } else {
- code = ""; //well, then no code
- }
- }
-
- if (code == "") {
- //use class directly, no code was found
- if (!isfunction) {
- for (const Map<StringName, Variant>::Element *E = scr->get_constants().front(); E; E = E->next()) {
- options.insert(E->key());
- }
- }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->get_argument_count())
- options.insert(String(E->key()) + "()");
- else
- options.insert(String(E->key()) + "(");
- }
-
- for (const Set<StringName>::Element *E = scr->get_members().front(); E; E = E->next()) {
- options.insert(E->get());
- }
- }
+ MethodInfo &mi = E->get();
+ String method_hint = mi.name;
+ if (method_hint.find(":") != -1) {
+ method_hint = method_hint.get_slice(":", 0);
+ }
+ method_hint += "(";
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ if (mi.arguments.size()) {
+ for (int i = 0; i < mi.arguments.size(); i++) {
+ if (i > 0) {
+ method_hint += ", ";
+ }
+ String arg = mi.arguments[i].name;
+ if (arg.find(":") != -1) {
+ arg = arg.substr(0, arg.find(":"));
+ }
+ method_hint += arg;
+ if (use_type_hint && mi.arguments[i].type != Variant::NIL) {
+ method_hint += " : ";
+ if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) {
+ method_hint += mi.arguments[i].class_name.operator String();
+ } else {
+ method_hint += Variant::get_type_name(mi.arguments[i].type);
}
}
}
-
- if (!isfunction) {
- ClassDB::get_integer_constant_list(t.obj_type, r_options);
-
- List<PropertyInfo> pinfo;
- ClassDB::get_property_list(t.obj_type, &pinfo);
-
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- r_options->push_back(E->get().name);
- }
+ }
+ method_hint += ")";
+ if (use_type_hint && (mi.return_val.type != Variant::NIL || !(mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
+ method_hint += " -> ";
+ if (mi.return_val.type == Variant::NIL) {
+ method_hint += "void";
+ } else if (mi.return_val.type == Variant::OBJECT && mi.return_val.class_name != StringName()) {
+ method_hint += mi.return_val.class_name.operator String();
+ } else {
+ method_hint += Variant::get_type_name(mi.return_val.type);
}
+ }
+ method_hint += ":";
- List<MethodInfo> mi;
- ClassDB::get_method_list(t.obj_type, &mi, false, true);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
-
- if (E->get().name.begins_with("_"))
- continue;
+ options.insert(method_hint);
+ }
+ } break;
+ case GDScriptParser::COMPLETION_YIELD: {
+ const GDScriptParser::Node *node = parser.get_completion_node();
- if (E->get().arguments.size())
- options.insert(E->get().name + "(");
- else
- options.insert(E->get().name + "()");
- }
- } else {
+ GDScriptCompletionContext c = context;
+ c.line = node->line;
+ GDScriptCompletionIdentifier type;
+ if (!_guess_expression_type(c, node, type)) {
+ break;
+ }
- //check InputEvent hint
- {
- if (t.value.get_type() == Variant::NIL) {
- Variant::CallError ce;
- t.value = Variant::construct(t.type, NULL, 0, ce);
+ GDScriptParser::DataType base_type = type.type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
+ options.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\"");
}
-
- if (!isfunction) {
- List<PropertyInfo> pl;
- t.value.get_property_list(&pl);
- for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
-
- if (String(E->get().name).find("/") == -1)
- options.insert(E->get().name);
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ List<MethodInfo> signals;
+ scr->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ options.insert("\"" + E->get().name + "\"");
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ base_type.has_type = false;
+
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ break;
}
}
- List<MethodInfo> mi;
- t.value.get_method_list(&mi);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
- if (E->get().arguments.size())
- options.insert(E->get().name + "(");
- else
- options.insert(E->get().name + "()");
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(class_name, &signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ options.insert("\"" + E->get().name + "\"");
}
+ } break;
+ default: {
+ base_type.has_type = false;
}
}
}
-
} break;
- case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
-
- _find_call_arguments(context, p.get_completion_node(), p.get_completion_line(), p.get_completion_argument_index(), options, r_forced, r_call_hint);
+ case GDScriptParser::COMPLETION_RESOURCE_PATH: {
+ if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
+ r_forced = true;
+ }
} break;
- case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
-
- GDScriptCompletionIdentifier cid = _get_native_class(context);
-
- if (cid.obj_type != StringName()) {
- List<MethodInfo> vm;
- ClassDB::get_virtual_methods(cid.obj_type, &vm);
- for (List<MethodInfo>::Element *E = vm.front(); E; E = E->next()) {
+ case GDScriptParser::COMPLETION_ASSIGN: {
+ GDScriptCompletionIdentifier type;
+ if (!_guess_expression_type(context, parser.get_completion_node(), type)) {
+ break;
+ }
- MethodInfo &mi = E->get();
- String m = mi.name;
- if (m.find(":") != -1)
- m = m.substr(0, m.find(":"));
- m += "(";
-
- if (mi.arguments.size()) {
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- m += ", ";
- String n = mi.arguments[i].name;
- if (n.find(":") != -1)
- n = n.substr(0, n.find(":"));
- m += n;
+ if (!type.enumeration.empty()) {
+ _find_enumeration_candidates(type.enumeration, options);
+ r_forced = options.size() > 0;
+ }
+ } break;
+ case GDScriptParser::COMPLETION_TYPE_HINT: {
+ const GDScriptParser::ClassNode *clss = context._class;
+ while (clss) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = clss->constant_expressions.front(); E; E = E->next()) {
+ GDScriptCompletionIdentifier constant;
+ GDScriptCompletionContext c = context;
+ c.function = NULL;
+ c.block = NULL;
+ c.line = E->value().expression->line;
+ if (_guess_expression_type(c, E->value().expression, constant)) {
+ if (constant.type.has_type && constant.type.is_meta_type) {
+ options.insert(E->key().operator String());
}
}
- m += "):";
+ }
+ for (int i = 0; i < clss->subclasses.size(); i++) {
+ if (clss->subclasses[i]->name != StringName()) {
+ options.insert(clss->subclasses[i]->name.operator String());
+ }
+ }
+ clss = clss->owner;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ options.insert(Variant::get_type_name((Variant::Type)i));
+ }
+ }
- options.insert(m);
+ List<StringName> native_classes;
+ ClassDB::get_class_list(&native_classes);
+ for (List<StringName>::Element *E = native_classes.front(); E; E = E->next()) {
+ String class_name = E->get().operator String();
+ if (class_name.begins_with("_")) {
+ class_name = class_name.right(1);
+ }
+ if (Engine::get_singleton()->has_singleton(class_name)) {
+ continue;
}
+ options.insert(class_name);
}
- } break;
- case GDScriptParser::COMPLETION_YIELD: {
- const GDScriptParser::Node *node = p.get_completion_node();
+ // Named scripts
+ List<StringName> named_scripts;
+ ScriptServer::get_global_class_list(&named_scripts);
+ for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
+ options.insert(E->get().operator String());
+ }
- GDScriptCompletionIdentifier t;
- if (!_guess_expression_type(context, node, p.get_completion_line(), t))
+ if (parser.get_completion_identifier_is_function()) {
+ options.insert("void");
+ }
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_TYPE_HINT_INDEX: {
+ GDScriptCompletionIdentifier base;
+ String index = parser.get_completion_cursor().operator String();
+ if (!_guess_identifier_type(context, index.get_slice(".", 0), base)) {
break;
+ }
- if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
+ GDScriptCompletionContext c = context;
+ c._class = NULL;
+ c.function = NULL;
+ c.block = NULL;
+ bool finding = true;
+ index = index.right(index.find(".") + 1);
+ while (index.find(".") != -1) {
+ String id = index.get_slice(".", 0);
- List<MethodInfo> sigs;
- ClassDB::get_signal_list(t.obj_type, &sigs);
- for (List<MethodInfo>::Element *E = sigs.front(); E; E = E->next()) {
- options.insert("\"" + E->get().name + "\"");
- r_forced = true;
+ GDScriptCompletionIdentifier sub_base;
+ if (!_guess_identifier_type_from_base(c, base, id, sub_base)) {
+ finding = false;
+ break;
}
+ index = index.right(index.find(".") + 1);
+ base = sub_base;
}
- } break;
- case GDScriptParser::COMPLETION_RESOURCE_PATH: {
-
- if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
- get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
- r_forced = true;
+ if (!finding) {
+ break;
}
- } break;
- case GDScriptParser::COMPLETION_ASSIGN: {
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, p.get_completion_node(), p.get_completion_line(), ci)) {
-
- String enumeration = ci.enumeration;
- if (enumeration.find(".") != -1) {
- //class constant
- List<StringName> constants;
- String cls = enumeration.get_slice(".", 0);
- String enm = enumeration.get_slice(".", 1);
-
- ClassDB::get_enum_constants(cls, enm, &constants);
- //constants.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- String add = cls + "." + E->get();
- r_options->push_back(add);
- r_forced = true;
- }
- } else {
-
- //global constant
- StringName current_enum = enumeration;
+ GDScriptParser::DataType base_type = base.type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = base_type.class_type->constant_expressions.front(); E; E = E->next()) {
+ GDScriptCompletionIdentifier constant;
+ GDScriptCompletionContext c = context;
+ c._class = base_type.class_type;
+ c.function = NULL;
+ c.block = NULL;
+ c.line = E->value().expression->line;
+ if (_guess_expression_type(c, E->value().expression, constant)) {
+ if (constant.type.has_type && constant.type.is_meta_type) {
+ options.insert(E->key().operator String());
+ }
+ }
+ }
+ for (int i = 0; i < base_type.class_type->subclasses.size(); i++) {
+ if (base_type.class_type->subclasses[i]->name != StringName()) {
+ options.insert(base_type.class_type->subclasses[i]->name.operator String());
+ }
+ }
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- r_options->push_back(GlobalConstants::get_global_constant_name(i));
- r_forced = true;
+ base_type = base_type.class_type->base_type;
+ } else {
+ base_type.has_type = false;
}
- }
- //global
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ Ref<Script> const_scr = E->value();
+ if (const_scr.is_valid()) {
+ options.insert(E->key().operator String());
+ }
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.has_type = false;
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
-#endif
+ r_forced = options.size() > 0;
} break;
}
@@ -2531,6 +2844,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
#endif
+//////// END COMPLETION //////////
+
String GDScriptLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@@ -2616,6 +2931,185 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
#ifdef TOOLS_ENABLED
+static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, bool p_is_function, GDScriptLanguage::LookupResult &r_result) {
+ GDScriptParser::DataType base_type = p_base;
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type) {
+ if (p_is_function) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->functions[i]->line;
+ return OK;
+ }
+ }
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->static_functions[i]->line;
+ return OK;
+ }
+ }
+ } else {
+ if (base_type.class_type->constant_expressions.has(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->constant_expressions[p_symbol].expression->line;
+ return OK;
+ }
+
+ for (int i = 0; i < base_type.class_type->variables.size(); i++) {
+ if (base_type.class_type->variables[i].identifier == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->variables[i].line;
+ return OK;
+ }
+ }
+ }
+ }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ int line = scr->get_member_line(p_symbol);
+ if (line >= 0) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = line;
+ r_result.script = scr;
+ return OK;
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ base_type.has_type = false;
+ break;
+ }
+ }
+
+ if (ClassDB::has_method(class_name, p_symbol, true)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ List<MethodInfo> virtual_methods;
+ ClassDB::get_virtual_methods(class_name, &virtual_methods, true);
+ for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
+ if (E->get().name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true);
+ if (enum_name != StringName()) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = enum_name;
+ return OK;
+ }
+
+ List<String> constants;
+ ClassDB::get_integer_constant_list(class_name, &constants, true);
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ if (E->get() == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(class_name, &properties, true);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ StringName parent = ClassDB::get_parent_class(class_name);
+ if (parent != StringName()) {
+ if (String(parent).begins_with("_")) {
+ base_type.native_type = String(parent).right(1);
+ } else {
+ base_type.native_type = parent;
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ base_type.has_type = false;
+
+ if (Variant::has_numeric_constant(base_type.builtin_type, p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ Variant v;
+ REF v_ref;
+ if (base_type.builtin_type == Variant::OBJECT) {
+ v_ref.instance();
+ v = v_ref;
+ } else {
+ Variant::CallError err;
+ v = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ break;
+ }
+ }
+
+ if (v.has_method(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ bool valid = false;
+ v.get(p_symbol, &valid);
+ if (valid) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
+ }
+ }
+
+ return ERR_CANT_RESOLVE;
+}
+
Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) {
//before parsing, try the usual stuff
@@ -2623,6 +3117,13 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
r_result.class_name = p_symbol;
return OK;
+ } else {
+ String under_prefix = "_" + p_symbol;
+ if (ClassDB::class_exists(under_prefix)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name = p_symbol;
+ return OK;
+ }
}
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@@ -2650,17 +3151,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
- GDScriptParser p;
- p.parse(p_code, p_base_path, false, "", true);
+ GDScriptParser parser;
+ parser.parse(p_code, p_base_path, false, "", true);
- if (p.get_completion_type() == GDScriptParser::COMPLETION_NONE)
+ if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) {
return ERR_CANT_RESOLVE;
+ }
GDScriptCompletionContext context;
-
- context._class = p.get_completion_class();
- context.block = p.get_completion_block();
- context.function = p.get_completion_function();
+ context._class = parser.get_completion_class();
+ context.function = parser.get_completion_function();
+ context.block = parser.get_completion_block();
+ context.line = parser.get_completion_line();
context.base = p_owner;
context.base_path = p_base_path;
@@ -2675,171 +3177,68 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- bool isfunction = false;
+ bool is_function = false;
- switch (p.get_completion_type()) {
-
- case GDScriptParser::COMPLETION_GET_NODE:
- case GDScriptParser::COMPLETION_NONE: {
- } break;
+ switch (parser.get_completion_type()) {
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
-
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = Variant::get_type_name(p.get_completion_built_in_constant());
+ r_result.class_name = Variant::get_type_name(parser.get_completion_built_in_constant());
r_result.class_member = p_symbol;
return OK;
-
} break;
+ case GDScriptParser::COMPLETION_PARENT_FUNCTION:
case GDScriptParser::COMPLETION_FUNCTION: {
-
- if (context._class && context._class->functions.size()) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->functions[i]->line;
- return OK;
- }
- }
- }
-
- Ref<GDScript> parent = _get_parent_class(context);
- while (parent.is_valid()) {
- int line = parent->get_member_line(p_symbol);
- if (line >= 0) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = line;
- r_result.script = parent;
- return OK;
- }
-
- parent = parent->get_base();
- }
-
- GDScriptCompletionIdentifier identifier = _get_native_class(context);
- print_line("identifier: " + String(identifier.obj_type));
-
- if (ClassDB::has_method(identifier.obj_type, p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = identifier.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- } break;
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_IDENTIFIER: {
- //check if a function
- if (p.get_completion_identifier_is_function()) {
- if (context._class && context._class->functions.size()) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->functions[i]->line;
- return OK;
- }
- }
- }
-
- Ref<GDScript> parent = _get_parent_class(context);
- while (parent.is_valid()) {
- int line = parent->get_member_line(p_symbol);
- if (line >= 0) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = line;
- r_result.script = parent;
- return OK;
- }
-
- parent = parent->get_base();
- }
-
- GDScriptCompletionIdentifier identifier = _get_native_class(context);
-
- if (ClassDB::has_method(identifier.obj_type, p_symbol)) {
+ if (!is_function) {
+ is_function = parser.get_completion_identifier_is_function();
+ }
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = identifier.obj_type;
- r_result.class_member = p_symbol;
- return OK;
+ GDScriptParser::DataType base_type;
+ if (context._class) {
+ if (parser.get_completion_type() != GDScriptParser::COMPLETION_PARENT_FUNCTION) {
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(context._class);
+ } else {
+ base_type = context._class->base_type;
}
} else {
+ break;
+ }
- GDScriptCompletionIdentifier gdi = _get_native_class(context);
- if (gdi.obj_type != StringName()) {
- bool valid;
- Variant::Type t = ClassDB::get_property_type(gdi.obj_type, p_symbol, &valid);
- if (t != Variant::NIL && valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = gdi.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
- }
-
+ if (!is_function && context.block) {
+ // Lookup local variables
const GDScriptParser::BlockNode *block = context.block;
- //search in blocks going up (local var?)
while (block) {
-
- for (int i = 0; i < block->statements.size(); i++) {
-
- if (block->statements[i]->line > p.get_completion_line())
- continue;
-
- if (block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
-
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(block->statements[i]);
-
- if (lv->assign && lv->name == p_symbol) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = block->statements[i]->line;
- return OK;
- }
- }
+ if (block->variables.has(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = block->variables[p_symbol]->line;
+ return OK;
}
block = block->parent_block;
}
+ }
- //guess from function arguments
- if (context.function && context.function->name != StringName()) {
-
- for (int i = 0; i < context.function->arguments.size(); i++) {
-
- if (context.function->arguments[i] == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context.function->line;
- return OK;
- }
- }
- }
-
- //guess in class constants
-
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
-
- if (context._class->constant_expressions[i].identifier == p_symbol) {
+ if (context.function && context.function->name != StringName()) {
+ // Lookup function arguments
+ for (int i = 0; i < context.function->arguments.size(); i++) {
+ if (context.function->arguments[i] == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->constant_expressions[i].expression->line;
+ r_result.location = context.function->line;
return OK;
}
}
+ }
- //guess in class variables
- if (!(context.function && context.function->_static)) {
-
- for (int i = 0; i < context._class->variables.size(); i++) {
-
- if (context._class->variables[i].identifier == p_symbol) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->variables[i].line;
- return OK;
- }
- }
- }
+ if (_lookup_symbol_from_base(base_type, p_symbol, is_function, r_result) == OK) {
+ return OK;
+ }
- //guess in autoloads as singletons
+ if (!is_function) {
+ // Guess in autoloads as singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2856,8 +3255,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
String script = path.substr(1, path.length());
if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
+ // Not a script, try find the script anyway,
+ // may have some success
script = script.get_basename() + ".gd";
}
@@ -2872,7 +3271,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- //global
+ // Global
Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
if (classes.has(p_symbol)) {
Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]];
@@ -2918,152 +3317,31 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
}
-
- } break;
- case GDScriptParser::COMPLETION_PARENT_FUNCTION: {
-
} break;
- case GDScriptParser::COMPLETION_METHOD:
- isfunction = true;
+ case GDScriptParser::COMPLETION_METHOD: {
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_INDEX: {
-
- const GDScriptParser::Node *node = p.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR)
+ const GDScriptParser::Node *node = parser.get_completion_node();
+ if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ break;
+ }
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], base)) {
break;
-
- GDScriptCompletionIdentifier t;
- if (_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], p.get_completion_line(), t)) {
-
- if (t.type == Variant::OBJECT && t.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = t.value;
- if (gdn.is_valid()) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = gdn->get_name();
- r_result.class_member = p_symbol;
- return OK;
- }
- } else if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
-
- Ref<GDScript> on_script;
-
- if (t.value.get_type()) {
- Object *obj = t.value;
-
- if (obj) {
-
- on_script = obj->get_script();
-
- if (on_script.is_valid()) {
- int loc = on_script->get_member_line(p_symbol);
- if (loc >= 0) {
- r_result.script = on_script;
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = loc;
- return OK;
- }
- }
- }
- }
-
- if (ClassDB::has_method(t.obj_type, p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true);
- if (enumName != StringName()) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
- r_result.class_name = t.obj_type;
- r_result.class_member = enumName;
- return OK;
- }
-
- bool success;
- ClassDB::get_integer_constant(t.obj_type, p_symbol, &success);
- if (success) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- ClassDB::get_property_type(t.obj_type, p_symbol, &success);
-
- if (success) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- } else {
-
- Variant::CallError ce;
- Variant v = Variant::construct(t.type, NULL, 0, ce);
-
- bool valid;
- v.get_numeric_constant_value(t.type, p_symbol, &valid);
- if (valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
-
- //todo check all inputevent types for property
-
- v.get(p_symbol, &valid);
-
- if (valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
-
- if (v.has_method(p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
- }
}
- } break;
- case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
-
- return ERR_CANT_RESOLVE;
+ if (_lookup_symbol_from_base(base.type, p_symbol, is_function, r_result) == OK) {
+ return OK;
+ }
} break;
case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
+ GDScriptParser::DataType base_type = context._class->base_type;
- GDScriptCompletionIdentifier cid = _get_native_class(context);
-
- if (cid.obj_type != StringName()) {
- List<MethodInfo> vm;
- ClassDB::get_virtual_methods(cid.obj_type, &vm);
- for (List<MethodInfo>::Element *E = vm.front(); E; E = E->next()) {
-
- if (p_symbol == E->get().name) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = cid.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
- }
+ if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) {
+ return OK;
}
} break;
- case GDScriptParser::COMPLETION_YIELD: {
-
- return ERR_CANT_RESOLVE;
-
- } break;
}
return ERR_CANT_RESOLVE;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 61130cb58f..6a08d86904 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -200,6 +200,12 @@ static String _get_var_type(const Variant *p_type) {
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
@@ -318,10 +324,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (_stack_size) {
stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++)
- memnew_placement(&stack[i], Variant(*p_args[i]));
- for (int i = p_argcount; i < _stack_size; i++)
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i], Variant(arg));
+ } else {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
+ }
} else {
stack = NULL;
}
@@ -709,6 +733,199 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ if (src->get_type() != var_type) {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ OPCODE_BREAK;
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+ Object *src_obj = src->operator Object *();
+ GD_ERR_BREAK(!src_obj);
+
+ if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+ if (!scr_inst) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+ Variant::CallError err;
+ *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Variant::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Invalid cast: can't convert a non-object value to an object type.";
+ OPCODE_BREAK;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_CONSTRUCT) {
CHECK_SPACE(2);
@@ -1370,6 +1587,15 @@ int GDScriptFunction::get_default_argument_addr(int p_idx) const {
return default_arguments[p_idx];
}
+GDScriptDataType GDScriptFunction::get_return_type() const {
+ return return_type;
+}
+
+GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, argument_types.size(), GDScriptDataType());
+ return argument_types[p_idx];
+}
+
StringName GDScriptFunction::get_name() const {
return name;
@@ -1552,7 +1778,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1560,10 +1786,10 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
@@ -1614,7 +1840,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1622,10 +1848,10 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 836325f0fe..3ce84290fd 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -42,6 +42,95 @@
class GDScriptInstance;
class GDScript;
+struct GDScriptDataType {
+ bool has_type;
+ enum {
+ BUILTIN,
+ NATIVE,
+ SCRIPT,
+ GDSCRIPT
+ } kind;
+ Variant::Type builtin_type;
+ StringName native_type;
+ Ref<Script> script_type;
+
+ bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
+ if (!has_type) return true; // Can't type check
+
+ switch (kind) {
+ case BUILTIN: {
+ Variant::Type var_type = p_variant.get_type();
+ bool valid = builtin_type == var_type;
+ if (!valid && p_allow_implicit_conversion) {
+ valid = Variant::can_convert_strict(var_type, builtin_type);
+ }
+ return valid;
+ } break;
+ case NATIVE: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
+ return false;
+ }
+ return true;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : NULL;
+ bool valid = false;
+ while (base.is_valid()) {
+ if (base == script_type) {
+ valid = true;
+ break;
+ }
+ base = base->get_base_script();
+ }
+ return valid;
+ } break;
+ }
+ return false;
+ }
+
+ operator PropertyInfo() const {
+ PropertyInfo info;
+ if (has_type) {
+ switch (kind) {
+ case BUILTIN: {
+ info.type = builtin_type;
+ } break;
+ case NATIVE: {
+ info.type = Variant::OBJECT;
+ info.class_name = native_type;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ info.type = Variant::OBJECT;
+ info.class_name = script_type->get_instance_base_type();
+ } break;
+ }
+ } else {
+ info.type = Variant::NIL;
+ info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ return info;
+ }
+
+ GDScriptDataType() :
+ has_type(false) {}
+};
+
class GDScriptFunction {
public:
enum Opcode {
@@ -56,6 +145,12 @@ public:
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
+ OPCODE_ASSIGN_TYPED_BUILTIN,
+ OPCODE_ASSIGN_TYPED_NATIVE,
+ OPCODE_ASSIGN_TYPED_SCRIPT,
+ OPCODE_CAST_TO_BUILTIN,
+ OPCODE_CAST_TO_NATIVE,
+ OPCODE_CAST_TO_SCRIPT,
OPCODE_CONSTRUCT, //only for basic types!!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
@@ -139,6 +234,8 @@ private:
#endif
Vector<int> default_arguments;
Vector<int> code;
+ Vector<GDScriptDataType> argument_types;
+ GDScriptDataType return_type;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
@@ -199,6 +296,8 @@ public:
int get_max_stack_size() const;
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
+ GDScriptDataType get_return_type() const;
+ GDScriptDataType get_argument_type(int p_idx) const;
GDScript *get_script() const { return _script; }
StringName get_source() const { return source; }
@@ -234,7 +333,7 @@ class GDScriptFunctionState : public Reference {
GDScriptFunction *function;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
- Ref<GDScriptFunctionState> previous_state;
+ Ref<GDScriptFunctionState> first_state;
protected:
static void _bind_methods();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index a88ba477c6..7e98b6ced9 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -105,6 +105,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"prints",
"printerr",
"printraw",
+ "print_debug",
"var2str",
"str2var",
"var2bytes",
@@ -120,6 +121,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"Color8",
"ColorN",
"print_stack",
+ "get_stack",
"instance_from_id",
"len",
"is_instance_valid",
@@ -701,6 +703,23 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
r_ret = Variant();
} break;
+ case TEXT_PRINT_DEBUG: {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+
+ str += p_args[i]->operator String();
+ }
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ if (script->debug_get_stack_level_count() > 0) {
+ str += "\n\t";
+ str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'";
+ }
+
+ //str+="\n";
+ print_line(str);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -1213,6 +1232,22 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
};
} break;
+ case GET_STACK: {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ Array ret;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ r_ret = ret;
+ } break;
+
case INSTANCE_FROM_ID: {
VALIDATE_ARG_COUNT(1);
@@ -1628,7 +1663,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "WeakRef";
+ mi.return_val.class_name = "WeakRef";
return mi;
@@ -1637,19 +1672,20 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "FuncRef";
+ mi.return_val.class_name = "FuncRef";
return mi;
} break;
case TYPE_CONVERT: {
- MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::INT, "type"));
- mi.return_val.type = Variant::OBJECT;
+ MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
+ mi.return_val.type = Variant::NIL;
+ mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
return mi;
} break;
case TYPE_OF: {
- MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what"));
+ MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
@@ -1716,8 +1752,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TEXT_PRINT_DEBUG: {
+
+ MethodInfo mi("print_debug");
+ mi.return_val.type = Variant::NIL;
+ mi.flags |= METHOD_FLAG_VARARG;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
- MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
@@ -1730,7 +1774,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
- MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi;
@@ -1753,7 +1797,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "Resource";
+ mi.return_val.class_name = "Resource";
return mi;
} break;
case INST2DICT: {
@@ -1783,13 +1827,13 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break;
case TO_JSON: {
- MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
} break;
case HASH: {
- MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;
@@ -1813,6 +1857,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::NIL;
return mi;
} break;
+ case GET_STACK: {
+ MethodInfo mi("get_stack");
+ mi.return_val.type = Variant::NIL;
+ return mi;
+ } break;
case INSTANCE_FROM_ID: {
MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
@@ -1820,7 +1869,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case LEN: {
- MethodInfo mi("len", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index c4731d17a4..a29f06e839 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -96,6 +96,7 @@ public:
TEXT_PRINT_SPACED,
TEXT_PRINTERR,
TEXT_PRINTRAW,
+ TEXT_PRINT_DEBUG,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
@@ -111,6 +112,7 @@ public:
COLOR8,
COLORN,
PRINT_STACK,
+ GET_STACK,
INSTANCE_FROM_ID,
LEN,
IS_INSTANCE_VALID,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index fdb92a68a9..ac53f33e9e 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,6 +30,10 @@
#include "gdscript_parser.h"
+#include "core/core_string_names.h"
+#include "core/engine.h"
+#include "core/project_settings.h"
+#include "core/reference.h"
#include "gdscript.h"
#include "io/resource_loader.h"
#include "os/file_access.h"
@@ -138,8 +142,9 @@ bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bo
}
Node *arg = _parse_expression(p_parent, p_static);
- if (!arg)
+ if (!arg) {
return false;
+ }
p_args.push_back(arg);
@@ -263,6 +268,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
bool need_identifier = true;
bool done = false;
+ int line = tokenizer->get_token_line();
while (!done) {
@@ -330,16 +336,19 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_CALL;
-
+ op->line = line;
op->arguments.push_back(alloc_node<SelfNode>());
+ op->arguments[0]->line = line;
IdentifierNode *funcname = alloc_node<IdentifierNode>();
funcname->name = "get_node";
-
+ funcname->line = line;
op->arguments.push_back(funcname);
ConstantNode *nodepath = alloc_node<ConstantNode>();
nodepath->value = NodePath(StringName(path));
+ nodepath->datatype = _type_from_variant(nodepath->value);
+ nodepath->line = line;
op->arguments.push_back(nodepath);
expr = op;
@@ -353,6 +362,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = tokenizer->get_token_constant();
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_PI) {
@@ -360,6 +370,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_PI;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_TAU) {
@@ -367,6 +378,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_TAU;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_INF) {
@@ -374,6 +386,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_INF;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_NAN) {
@@ -381,6 +394,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_NAN;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_PRELOAD) {
@@ -419,15 +433,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (subexpr->type == Node::TYPE_IDENTIFIER) {
IdentifierNode *in = static_cast<IdentifierNode *>(subexpr);
- Vector<ClassNode::Constant> ce = current_class->constant_expressions;
// Try to find the constant expression by the identifier
- for (int i = 0; i < ce.size(); ++i) {
- if (ce[i].identifier == in->name) {
- if (ce[i].expression->type == Node::TYPE_CONSTANT) {
- cn = static_cast<ConstantNode *>(ce[i].expression);
- found_constant = true;
- }
+ if (current_class->constant_expressions.has(in->name)) {
+ Node *cn_exp = current_class->constant_expressions[in->name].expression;
+ if (cn_exp->type == Node::TYPE_CONSTANT) {
+ cn = static_cast<ConstantNode *>(cn_exp);
+ found_constant = true;
}
}
}
@@ -480,15 +492,28 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
_set_error("Expected ')' after 'preload' path");
return NULL;
}
+
+ Ref<GDScript> gds = res;
+ if (gds.is_valid() && !gds->is_valid()) {
+ _set_error("Could not fully preload the script, possible cyclic reference or compilation error.");
+ return NULL;
+ }
+
tokenizer->advance();
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = res;
+ constant->datatype = _type_from_variant(constant->value);
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_YIELD) {
- //constant defined by tokenizer
+ if (!current_function) {
+ _set_error("yield() can only be used inside function blocks.");
+ return NULL;
+ }
+
+ current_function->has_yield = true;
tokenizer->advance();
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
@@ -618,6 +643,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = Variant::get_numeric_constant_value(bi_type, identifier);
+ cn->datatype = _type_from_variant(cn->value);
expr = cn;
}
@@ -695,24 +721,56 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
const ClassNode *cln = current_class;
bool bfn = false;
StringName identifier;
+ int id_line = tokenizer->get_token_line();
if (_get_completable_identifier(COMPLETION_IDENTIFIER, identifier)) {
}
- if (p_parsing_constant) {
- for (int i = 0; i < cln->constant_expressions.size(); ++i) {
-
- if (cln->constant_expressions[i].identifier == identifier) {
+ BlockNode *b = current_block;
+ while (b) {
+ if (b->variables.has(identifier)) {
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ LocalVarNode *lv = b->variables[identifier];
+ id->name = identifier;
+ id->declared_block = b;
+ id->line = id_line;
+ expr = id;
+ bfn = true;
- expr = cln->constant_expressions[i].expression;
- bfn = true;
- break;
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SUB: {
+ if (lv->assignments == 0 && !lv->datatype.has_type) {
+ _set_error("Using assignment with operation on a variable that was never assigned.");
+ return NULL;
+ }
+ } // fallthrough
+ case GDScriptTokenizer::TK_OP_ASSIGN: {
+ lv->assignments += 1;
+ }
}
+ break;
}
+ b = b->parent_block;
+ }
- if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ if (!bfn && p_parsing_constant) {
+ if (cln->constant_expressions.has(identifier)) {
+ expr = cln->constant_expressions[identifier].expression;
+ bfn = true;
+ } else if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
//check from constants
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = GDScriptLanguage::get_singleton()->get_global_array()[GDScriptLanguage::get_singleton()->get_global_map()[identifier]];
+ constant->datatype = _type_from_variant(constant->value);
+ constant->line = id_line;
expr = constant;
bfn = true;
}
@@ -729,6 +787,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (!bfn) {
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = identifier;
+ id->line = id_line;
expr = id;
}
@@ -902,6 +961,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//lua style identifier, easier to write
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = tokenizer->get_token_literal();
+ cn->datatype = _type_from_variant(cn->value);
key = cn;
tokenizer->advance(2);
expecting = DICT_EXPECT_VALUE;
@@ -941,7 +1001,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = dict;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR) && tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR)) {
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
// parent call
@@ -953,17 +1013,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(self);
forbidden for now */
StringName identifier;
- if (_get_completable_identifier(COMPLETION_PARENT_FUNCTION, identifier)) {
- //indexing stuff
- }
+ bool is_completion = _get_completable_identifier(COMPLETION_PARENT_FUNCTION, identifier) && for_completion;
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = identifier;
op->arguments.push_back(id);
- tokenizer->advance(1);
- if (!_parse_arguments(op, op->arguments, p_static))
- return NULL;
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
+ if (!is_completion) {
+ _set_error("Expected '(' for parent function call.");
+ return NULL;
+ }
+ } else {
+ tokenizer->advance();
+ if (!_parse_arguments(op, op->arguments, p_static)) {
+ return NULL;
+ }
+ }
expr = op;
@@ -1087,6 +1153,26 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
break;
}
+ /*****************/
+ /* Parse Casting */
+ /*****************/
+
+ bool has_casting = expr->type == Node::TYPE_CAST;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_AS) {
+ if (has_casting) {
+ _set_error("Unexpected 'as'.");
+ return NULL;
+ }
+ CastNode *cn = alloc_node<CastNode>();
+ if (!_parse_type(cn->cast_type)) {
+ _set_error("Expected type after 'as'.");
+ return NULL;
+ }
+ has_casting = true;
+ cn->source_node = expr;
+ expr = cn;
+ }
+
/******************/
/* Parse Operator */
/******************/
@@ -1110,7 +1196,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//assign, if allowed is only allowed on the first operator
#define _VALIDATE_ASSIGN \
- if (!p_allow_assign) { \
+ if (!p_allow_assign || has_casting) { \
_set_error("Unexpected assign."); \
return NULL; \
} \
@@ -1338,11 +1424,11 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (next_op >= (expression.size() - 2) || expression[next_op + 2].op != OperatorNode::OP_TERNARY_ELSE) {
_set_error("Expected else after ternary if.");
- ERR_FAIL_V(NULL);
+ return NULL;
}
if (next_op >= (expression.size() - 3)) {
_set_error("Expected value after ternary else.");
- ERR_FAIL_V(NULL);
+ return NULL;
}
OperatorNode *op = alloc_node<OperatorNode>();
@@ -1450,13 +1536,13 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
Array arr;
- //print_line("mk array "+itos(!p_to_const));
arr.resize(an->elements.size());
for (int i = 0; i < an->elements.size(); i++) {
ConstantNode *acn = static_cast<ConstantNode *>(an->elements[i]);
arr[i] = acn->value;
}
cn->value = arr;
+ cn->datatype = _type_from_variant(cn->value);
return cn;
}
@@ -1490,6 +1576,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
dict[key_c->value] = value_c->value;
}
cn->value = dict;
+ cn->datatype = _type_from_variant(cn->value);
return cn;
}
@@ -1591,6 +1678,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
} else if (op->arguments[0]->type == Node::TYPE_BUILT_IN_FUNCTION && last_not_constant == 0) {
@@ -1620,24 +1708,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
-
- } /*else if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
-
- ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
- IdentifierNode *ib = static_cast<IdentifierNode*>(op->arguments[1]);
-
- bool valid;
- Variant v = ca->value.get_named(ib->name,&valid);
- if (!valid) {
- _set_error("invalid index '"+String(ib->name)+"' in constant expression");
- return op;
- }
-
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value=v;
- return cn;
- }*/
+ }
return op;
@@ -1658,13 +1731,14 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
}
return op;
}
- //validate assignment (don't assign to cosntant expression
+ //validate assignment (don't assign to constant expression
switch (op->op) {
case OperatorNode::OP_ASSIGN:
@@ -1711,6 +1785,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
} \
ConstantNode *cn = alloc_node<ConstantNode>(); \
cn->value = res; \
+ cn->datatype = _type_from_variant(res); \
return cn;
#define _REDUCE_BINARY(m_vop) \
@@ -1724,6 +1799,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
} \
ConstantNode *cn = alloc_node<ConstantNode>(); \
cn->value = res; \
+ cn->datatype = _type_from_variant(res); \
return cn;
switch (op->op) {
@@ -1799,6 +1875,13 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
case OperatorNode::OP_BIT_XOR: {
_REDUCE_BINARY(Variant::OP_BIT_XOR);
} break;
+ case OperatorNode::OP_TERNARY_IF: {
+ if (static_cast<ConstantNode *>(op->arguments[0])->value.booleanize()) {
+ return op->arguments[1];
+ } else {
+ return op->arguments[2];
+ }
+ } break;
default: { ERR_FAIL_V(op); }
}
@@ -1905,6 +1988,15 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) {
tokenizer->advance();
pattern->pt_type = GDScriptParser::PatternNode::PT_BIND;
pattern->bind = tokenizer->get_token_identifier();
+ // Check if binding is already used
+ if (current_block->variables.has(pattern->bind)) {
+ _set_error("Binding name of '" + pattern->bind.operator String() + "' was already used in the pattern.");
+ return NULL;
+ }
+ // Create local variable for proper identifier detection later
+ LocalVarNode *lv = alloc_node<LocalVarNode>();
+ lv->name = pattern->bind;
+ current_block->variables.insert(lv->name, lv);
tokenizer->advance();
} break;
// dictionary
@@ -2038,18 +2130,34 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran
}
PatternBranchNode *branch = alloc_node<PatternBranchNode>();
+ branch->body = alloc_node<BlockNode>();
+ branch->body->parent_block = p_block;
+ p_block->sub_blocks.push_back(branch->body);
+ current_block = branch->body;
branch->patterns.push_back(_parse_pattern(p_static));
if (!branch->patterns[0]) {
return;
}
+ bool has_binding = branch->patterns[0]->pt_type == PatternNode::PT_BIND;
+ bool catch_all = has_binding || branch->patterns[0]->pt_type == PatternNode::PT_WILDCARD;
+
while (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
tokenizer->advance();
branch->patterns.push_back(_parse_pattern(p_static));
if (!branch->patterns[branch->patterns.size() - 1]) {
return;
}
+
+ PatternNode::PatternType pt = branch->patterns[branch->patterns.size() - 1]->pt_type;
+
+ if (pt == PatternNode::PT_BIND) {
+ _set_error("Cannot use bindings with multipattern.");
+ return;
+ }
+
+ catch_all = catch_all || pt == PatternNode::PT_WILDCARD;
}
if (!_enter_indent_block()) {
@@ -2057,54 +2165,76 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran
return;
}
- branch->body = alloc_node<BlockNode>();
- branch->body->parent_block = p_block;
- p_block->sub_blocks.push_back(branch->body);
- current_block = branch->body;
-
_parse_block(branch->body, p_static);
current_block = p_block;
+ if (catch_all && branch->body->has_return) {
+ p_block->has_return = true;
+ }
+
p_branches.push_back(branch);
}
}
void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings) {
+
+ const DataType &to_match_type = p_node_to_match->get_datatype();
+
switch (p_pattern->pt_type) {
case PatternNode::PT_CONSTANT: {
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
+ DataType pattern_type = _reduce_node_type(p_pattern->constant);
+ if (error_set) {
+ return;
+ }
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
+ OperatorNode *type_comp = NULL;
- OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
- typeof_pattern_value->op = OperatorNode::OP_CALL;
- typeof_pattern_value->arguments.push_back(typeof_node);
- typeof_pattern_value->arguments.push_back(p_pattern->constant);
+ // static type check if possible
+ if (pattern_type.has_type && to_match_type.has_type) {
+ if (!_is_type_compatible(to_match_type, pattern_type) && !_is_type_compatible(pattern_type, to_match_type)) {
+ _set_error("Pattern type (" + pattern_type.to_string() + ") is not compatible with the type of the value to match (" + to_match_type.to_string() + ").",
+ p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_pattern_value);
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
- // comare the actual values
+ OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
+ typeof_pattern_value->op = OperatorNode::OP_CALL;
+ typeof_pattern_value->arguments.push_back(typeof_node);
+ typeof_pattern_value->arguments.push_back(p_pattern->constant);
+
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_pattern_value);
+ }
+
+ // compare the actual values
OperatorNode *value_comp = alloc_node<OperatorNode>();
value_comp->op = OperatorNode::OP_EQUAL;
value_comp->arguments.push_back(p_pattern->constant);
value_comp->arguments.push_back(p_node_to_match);
- OperatorNode *comparison = alloc_node<OperatorNode>();
- comparison->op = OperatorNode::OP_AND;
- comparison->arguments.push_back(type_comp);
- comparison->arguments.push_back(value_comp);
+ if (type_comp) {
+ OperatorNode *full_comparison = alloc_node<OperatorNode>();
+ full_comparison->op = OperatorNode::OP_AND;
+ full_comparison->arguments.push_back(type_comp);
+ full_comparison->arguments.push_back(value_comp);
- p_resulting_node = comparison;
+ p_resulting_node = full_comparison;
+ } else {
+ p_resulting_node = value_comp;
+ }
} break;
case PatternNode::PT_BIND: {
@@ -2129,22 +2259,32 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
{
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
- typeof_array->name = "TYPE_ARRAY";
-
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_array);
+ OperatorNode *type_comp = NULL;
+ // static type check if possible
+ if (to_match_type.has_type) {
+ // must be an array
+ if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::ARRAY) {
+ _set_error("Cannot match an array pattern with a non-array expression.", p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
+ typeof_array->name = "TYPE_ARRAY";
+
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_array);
+ }
// size
ConstantNode *length = alloc_node<ConstantNode>();
@@ -2163,12 +2303,16 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
length_comparison->arguments.push_back(call);
length_comparison->arguments.push_back(length);
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
+ if (type_comp) {
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
- p_resulting_node = type_and_length_comparison;
+ p_resulting_node = type_and_length_comparison;
+ } else {
+ p_resulting_node = length_comparison;
+ }
}
for (int i = 0; i < p_pattern->array.size(); i++) {
@@ -2208,22 +2352,32 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
{
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
- typeof_dictionary->name = "TYPE_DICTIONARY";
-
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_dictionary);
+ OperatorNode *type_comp = NULL;
+ // static type check if possible
+ if (to_match_type.has_type) {
+ // must be an dictionary
+ if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::DICTIONARY) {
+ _set_error("Cannot match an dictionary pattern with a non-dictionary expression.", p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
+ typeof_dictionary->name = "TYPE_DICTIONARY";
+
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_dictionary);
+ }
// size
ConstantNode *length = alloc_node<ConstantNode>();
@@ -2242,12 +2396,16 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
length_comparison->arguments.push_back(call);
length_comparison->arguments.push_back(length);
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
+ if (type_comp) {
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
- p_resulting_node = type_and_length_comparison;
+ p_resulting_node = type_and_length_comparison;
+ } else {
+ p_resulting_node = length_comparison;
+ }
}
for (Map<ConstantNode *, PatternNode *>::Element *e = p_pattern->dictionary.front(); e; e = e->next()) {
@@ -2308,9 +2466,20 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
}
}
-void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement) {
+void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) {
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = "#match_value";
+ id->line = p_match_statement->line;
+ id->datatype = _reduce_node_type(p_match_statement->val_to_match);
+ if (id->datatype.has_type) {
+ _mark_line_as_safe(id->line);
+ } else {
+ _mark_line_as_unsafe(id->line);
+ }
+
+ if (error_set) {
+ return;
+ }
for (int i = 0; i < p_match_statement->branches.size(); i++) {
@@ -2323,11 +2492,16 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
for (int j = 0; j < branch->patterns.size(); j++) {
PatternNode *pattern = branch->patterns[j];
+ _mark_line_as_safe(pattern->line);
Map<StringName, Node *> bindings;
- Node *resulting_node;
+ Node *resulting_node = NULL;
_generate_pattern(pattern, id, resulting_node, bindings);
+ if (!resulting_node) {
+ return;
+ }
+
if (!binding.empty() && !bindings.empty()) {
_set_error("Multipatterns can't contain bindings");
return;
@@ -2335,6 +2509,14 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
binding = bindings;
}
+ // Result is always a boolean
+ DataType resulting_node_type;
+ resulting_node_type.has_type = true;
+ resulting_node_type.is_constant = true;
+ resulting_node_type.kind = DataType::BUILTIN;
+ resulting_node_type.builtin_type = Variant::BOOL;
+ resulting_node->set_datatype(resulting_node_type);
+
if (compiled_branch.compiled_pattern) {
OperatorNode *or_node = alloc_node<OperatorNode>();
or_node->op = OperatorNode::OP_OR;
@@ -2350,12 +2532,19 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
// prepare the body ...hehe
for (Map<StringName, Node *>::Element *e = binding.front(); e; e = e->next()) {
- LocalVarNode *local_var = alloc_node<LocalVarNode>();
- local_var->name = e->key();
+ if (!branch->body->variables.has(e->key())) {
+ _set_error("Parser bug: missing pattern bind variable.", branch->line);
+ ERR_FAIL();
+ }
+
+ LocalVarNode *local_var = branch->body->variables[e->key()];
local_var->assign = e->value();
+ local_var->set_datatype(local_var->assign->get_datatype());
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = local_var->name;
+ id->declared_block = branch->body;
+ id->set_datatype(local_var->assign->get_datatype());
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_ASSIGN;
@@ -2413,7 +2602,21 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
}
switch (token) {
-
+ case GDScriptTokenizer::TK_EOF:
+ case GDScriptTokenizer::TK_ERROR:
+ case GDScriptTokenizer::TK_NEWLINE:
+ case GDScriptTokenizer::TK_CF_PASS: {
+ // will check later
+ } break;
+ default: {
+ // TODO: Make this a warning
+ /*if (p_block->has_return) {
+ _set_error("Unreacheable code.");
+ return;
+ }*/
+ } break;
+ }
+ switch (token) {
case GDScriptTokenizer::TK_EOF:
p_block->end_line = tokenizer->get_token_line();
case GDScriptTokenizer::TK_ERROR: {
@@ -2443,6 +2646,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
_set_error("Expected ';' or <NewLine>.");
return;
}
+ _mark_line_as_safe(tokenizer->get_token_line());
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
// Ignore semicolon after 'pass'
@@ -2453,6 +2657,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
//variale declaration and (eventual) initialization
tokenizer->advance();
+ int var_line = tokenizer->get_token_line();
if (!tokenizer->is_token_literal(0, true)) {
_set_error("Expected identifier for local variable name.");
@@ -2470,24 +2675,34 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
}
BlockNode *check_block = p_block;
while (check_block) {
- for (int i = 0; i < check_block->variables.size(); i++) {
- if (n == check_block->variables[i]) {
- _set_error("Variable '" + String(n) + "' already defined in the scope (at line: " + itos(check_block->variable_lines[i]) + ").");
- return;
- }
+ if (check_block->variables.has(n)) {
+ _set_error("Variable '" + String(n) + "' already defined in the scope (at line: " + itos(check_block->variables[n]->line) + ").");
+ return;
}
check_block = check_block->parent_block;
}
- int var_line = tokenizer->get_token_line();
-
//must know when the local variable is declared
LocalVarNode *lv = alloc_node<LocalVarNode>();
lv->name = n;
+ lv->line = var_line;
p_block->statements.push_back(lv);
Node *assigned = NULL;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ lv->datatype = DataType();
+#ifdef DEBUG_ENABLED
+ lv->datatype.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(lv->datatype)) {
+ _set_error("Expected type for variable.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
tokenizer->advance();
@@ -2499,26 +2714,36 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
- lv->assign = subexpr;
+ lv->assignments++;
assigned = subexpr;
} else {
ConstantNode *c = alloc_node<ConstantNode>();
- c->value = Variant();
+ if (lv->datatype.has_type && lv->datatype.kind == DataType::BUILTIN) {
+ Variant::CallError err;
+ c->value = Variant::construct(lv->datatype.builtin_type, NULL, 0, err);
+ } else {
+ c->value = Variant();
+ }
+ c->line = var_line;
assigned = c;
}
//must be added later, to avoid self-referencing.
- p_block->variables.push_back(n); //line?
- p_block->variable_lines.push_back(var_line);
+ p_block->variables.insert(n, lv);
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = n;
+ id->declared_block = p_block;
+ id->line = var_line;
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_ASSIGN;
op->arguments.push_back(id);
op->arguments.push_back(assigned);
+ op->line = var_line;
p_block->statements.push_back(op);
+ lv->assign_op = op;
+ lv->assign = assigned;
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
@@ -2563,6 +2788,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
p_block->statements.push_back(cf_if);
+ bool all_have_return = cf_if->body->has_return;
+ bool have_else = false;
+
while (true) {
while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline())
@@ -2619,6 +2847,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
if (error_set)
return;
+ all_have_return = all_have_return && cf_else->body->has_return;
+
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELSE) {
if (tab_level.back()->get() > indent_level) {
@@ -2642,12 +2872,19 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
if (error_set)
return;
+ all_have_return = all_have_return && cf_if->body_else->has_return;
+ have_else = true;
+
break; //after else, exit
} else
break;
}
+ cf_if->body->has_return = all_have_return;
+ // If there's no else block, path out of the if might not have a return
+ p_block->has_return = all_have_return && have_else;
+
} break;
case GDScriptTokenizer::TK_CF_WHILE: {
@@ -2680,6 +2917,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
current_block = p_block;
if (error_set)
return;
+ p_block->has_return = cf_while->body->has_return;
p_block->statements.push_back(cf_while);
} break;
case GDScriptTokenizer::TK_CF_FOR: {
@@ -2711,6 +2949,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
+ DataType iter_type;
+ iter_type.is_constant = true;
+
if (container->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(container);
@@ -2745,6 +2986,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
case 2: cn->value = Vector2(constants[0], constants[1]); break;
case 3: cn->value = Vector3(constants[0], constants[1], constants[2]); break;
}
+ cn->datatype = _type_from_variant(cn->value);
container = cn;
} else {
OperatorNode *on = alloc_node<OperatorNode>();
@@ -2766,6 +3008,10 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
container = on;
}
}
+
+ iter_type.has_type = true;
+ iter_type.kind = DataType::BUILTIN;
+ iter_type.builtin_type = Variant::INT;
}
}
@@ -2789,15 +3035,20 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
// this is for checking variable for redefining
// inside this _parse_block
- cf_for->body->variables.push_back(id->name);
- cf_for->body->variable_lines.push_back(id->line);
+ LocalVarNode *lv = alloc_node<LocalVarNode>();
+ lv->name = id->name;
+ lv->line = id->line;
+ lv->assignments++;
+ id->declared_block = cf_for->body;
+ lv->set_datatype(iter_type);
+ id->set_datatype(iter_type);
+ cf_for->body->variables.insert(id->name, lv);
_parse_block(cf_for->body, p_static);
- cf_for->body->variables.remove(0);
- cf_for->body->variable_lines.remove(0);
current_block = p_block;
if (error_set)
return;
+ p_block->has_return = cf_for->body->has_return;
p_block->statements.push_back(cf_for);
} break;
case GDScriptTokenizer::TK_CF_CONTINUE: {
@@ -2827,6 +3078,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
tokenizer->advance();
ControlFlowNode *cf_return = alloc_node<ControlFlowNode>();
cf_return->cf_type = ControlFlowNode::CF_RETURN;
+ cf_return->line = tokenizer->get_token_line(-1);
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON || tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE || tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
//expect end of statement
@@ -2850,6 +3102,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
}
+ p_block->has_return = true;
} break;
case GDScriptTokenizer::TK_CF_MATCH: {
@@ -2882,12 +3135,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
_parse_pattern_block(compiled_branches, match_node->branches, p_static);
- _transform_match_statment(compiled_branches, match_node);
+ if (error_set) return;
ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
match_cf_node->match = match_node;
+ match_cf_node->body = compiled_branches;
+ p_block->has_return = p_block->has_return || compiled_branches->has_return;
p_block->statements.push_back(match_cf_node);
_end_statement();
@@ -2938,16 +3193,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
}
} break;
- /*
- case GDScriptTokenizer::TK_CF_LOCAL: {
-
- if (tokenizer->get_token(1)!=GDScriptTokenizer::TK_SEMICOLON && tokenizer->get_token(1)!=GDScriptTokenizer::TK_NEWLINE ) {
-
- _set_error("Expected ';' or <NewLine>.");
- }
- tokenizer->advance();
- } break;
- */
}
}
}
@@ -3103,6 +3348,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
case GDScriptTokenizer::TK_PR_EXTENDS: {
+ _mark_line_as_safe(tokenizer->get_token_line());
_parse_extends(p_class);
if (error_set)
return;
@@ -3112,6 +3358,33 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
} break;
+ case GDScriptTokenizer::TK_PR_CLASS_NAME: {
+
+ if (p_class->owner) {
+ _set_error("'class_name' is only valid for the main class namespace.");
+ return;
+ }
+ if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
+
+ _set_error("'class_name' syntax: 'class_name <UniqueName>'");
+ return;
+ }
+
+ p_class->name = tokenizer->get_token_identifier(1);
+
+ if (self_path != String() && ScriptServer::is_global_class(p_class->name) && ScriptServer::get_global_class_path(p_class->name) != self_path) {
+ _set_error("Unique global class '" + p_class->name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
+ return;
+ }
+
+ if (ClassDB::class_exists(p_class->name)) {
+ _set_error("Class '" + p_class->name + "' shadows a native class.");
+ return;
+ }
+
+ tokenizer->advance(2);
+
+ } break;
case GDScriptTokenizer::TK_PR_TOOL: {
if (p_class->tool) {
@@ -3128,7 +3401,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
//class inside class :D
StringName name;
- StringName extends;
if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
@@ -3138,6 +3410,31 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
name = tokenizer->get_token_identifier(1);
tokenizer->advance(2);
+ // Check if name is shadowing something else
+ if (ClassDB::class_exists(name) || ClassDB::class_exists("_" + name.operator String())) {
+ _set_error("Class '" + String(name) + "' shadows a native class.");
+ return;
+ }
+ if (ScriptServer::is_global_class(name)) {
+ _set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
+ return;
+ }
+ ClassNode *outer_class = p_class;
+ while (outer_class) {
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i]->name == name) {
+ _set_error("Another class named '" + String(name) + "' already exists in this scope (at line " + itos(outer_class->subclasses[i]->line) + ").");
+ return;
+ }
+ }
+ if (outer_class->constant_expressions.has(name)) {
+ _set_error("A constant named '" + String(name) + "' already exists in the outer class scope (at line" + itos(outer_class->constant_expressions[name].expression->line) + ").");
+ return;
+ }
+
+ outer_class = outer_class->owner;
+ }
+
ClassNode *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>();
newclass->initializer->parent_class = newclass;
@@ -3222,6 +3519,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
Vector<StringName> arguments;
+ Vector<DataType> argument_types;
Vector<Node *> default_values;
int fnline = tokenizer->get_token_line();
@@ -3252,6 +3550,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
+ DataType argtype;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (!_parse_type(argtype)) {
+ _set_error("Expected type for argument.");
+ return;
+ }
+ }
+ argument_types.push_back(argtype);
+
if (defaulting && tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
_set_error("Default parameter expected.");
@@ -3269,9 +3576,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
OperatorNode *on = alloc_node<OperatorNode>();
on->op = OperatorNode::OP_ASSIGN;
+ on->line = fnline;
IdentifierNode *in = alloc_node<IdentifierNode>();
in->name = argname;
+ in->line = fnline;
on->arguments.push_back(in);
on->arguments.push_back(defval);
@@ -3308,6 +3617,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (name == "_init") {
+ if (_static) {
+ _set_error("Constructor cannot be static.");
+ return;
+ }
+
if (p_class->extends_used) {
OperatorNode *cparent = alloc_node<OperatorNode>();
@@ -3322,6 +3636,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
_set_error("expected '(' for parent constructor arguments.");
+ return;
}
tokenizer->advance();
@@ -3359,6 +3674,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+ DataType return_type;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_FORWARD_ARROW) {
+
+ if (!_parse_type(return_type, true)) {
+ _set_error("Expected return type for function.");
+ return;
+ }
+ }
+
if (!_enter_indent_block(block)) {
_set_error("Indented block expected.");
@@ -3367,7 +3691,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
FunctionNode *function = alloc_node<FunctionNode>();
function->name = name;
+ function->return_type = return_type;
function->arguments = arguments;
+ function->argument_types = argument_types;
function->default_values = default_values;
function->_static = _static;
function->line = fnline;
@@ -4082,10 +4408,37 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member.line = tokenizer->get_token_line();
member.rpc_mode = rpc_mode;
+ if (current_class->constant_expressions.has(member.identifier)) {
+ _set_error("A constant named '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ itos(current_class->constant_expressions[member.identifier].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == member.identifier) {
+ _set_error("Variable '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ member.data_type = DataType();
+#ifdef DEBUG_ENABLED
+ member.data_type.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(member.data_type)) {
+ _set_error("Expected type for class variable.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
@@ -4093,7 +4446,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
#endif
tokenizer->advance();
- Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport);
+ Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL);
if (!subexpr) {
if (_recover_from_completion()) {
break;
@@ -4117,42 +4470,31 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member.expression = subexpr;
- if (autoexport) {
- if (1) /*(subexpr->type==Node::TYPE_ARRAY) {
-
- member._export.type=Variant::ARRAY;
-
- } else if (subexpr->type==Node::TYPE_DICTIONARY) {
+ if (autoexport && !member.data_type.has_type) {
- member._export.type=Variant::DICTIONARY;
-
- } else*/
- {
-
- if (subexpr->type != Node::TYPE_CONSTANT) {
+ if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Type-less export needs a constant expression assigned to infer type.");
- return;
- }
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
+ }
- ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
- if (cn->value.get_type() == Variant::NIL) {
+ ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
+ if (cn->value.get_type() == Variant::NIL) {
- _set_error("Can't accept a null constant expression for inferring export type.");
+ _set_error("Can't accept a null constant expression for inferring export type.");
+ return;
+ }
+ member._export.type = cn->value.get_type();
+ member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type() == Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res == NULL) {
+ _set_error("Exported constant not a type or resource.");
return;
}
- member._export.type = cn->value.get_type();
- member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- if (cn->value.get_type() == Variant::OBJECT) {
- Object *obj = cn->value;
- Resource *res = Object::cast_to<Resource>(obj);
- if (res == NULL) {
- _set_error("Exported constant not a type or resource.");
- return;
- }
- member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member._export.hint_string = res->get_class();
- }
+ member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string = res->get_class();
}
}
#ifdef TOOLS_ENABLED
@@ -4186,15 +4528,36 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
else
p_class->initializer->statements.push_back(op);
- } else {
+ member.initial_assignment = op;
- if (autoexport) {
+ } else {
+ if (autoexport && !member.data_type.has_type) {
_set_error("Type-less export needs a constant expression assigned to infer type.");
return;
}
}
+ if (autoexport && member.data_type.has_type) {
+ if (member.data_type.kind == DataType::BUILTIN) {
+ member._export.type = member.data_type.builtin_type;
+ } else if (member.data_type.kind == DataType::NATIVE) {
+ if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) {
+ member._export.type = Variant::OBJECT;
+ member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+ member._export.class_name = member.data_type.native_type;
+ } else {
+ _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
+ return;
+ }
+
+ } else {
+ _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_SETGET) {
tokenizer->advance();
@@ -4231,7 +4594,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
} break;
case GDScriptTokenizer::TK_PR_CONST: {
- //variale declaration and (eventual) initialization
+ // constant declaration and initialization
ClassNode::Constant constant;
@@ -4242,9 +4605,38 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- constant.identifier = tokenizer->get_token_literal();
+ StringName const_id = tokenizer->get_token_literal();
+ int line = tokenizer->get_token_line();
+
+ if (current_class->constant_expressions.has(const_id)) {
+ _set_error("Constant '" + String(const_id) + "' alread exists in this class (at line: " +
+ itos(current_class->constant_expressions[const_id].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == const_id) {
+ _set_error("A variable named '" + String(const_id) + "' alread exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ constant.type = DataType();
+#ifdef DEBUG_ENABLED
+ constant.type.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(constant.type)) {
+ _set_error("Expected type for class constant.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
_set_error("Constant expects assignment.");
return;
@@ -4261,14 +4653,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Expected constant expression");
+ _set_error("Expected constant expression", line);
+ return;
}
+ subexpr->line = line;
constant.expression = subexpr;
- p_class->constant_expressions.push_back(constant);
+ p_class->constant_expressions.insert(const_id, constant);
if (!_end_statement()) {
- _set_error("Expected end of statement (constant)");
+ _set_error("Expected end of statement (constant)", line);
return;
}
@@ -4311,7 +4705,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} else { // tokenizer->is_token_literal(0, true)
ClassNode::Constant constant;
- constant.identifier = tokenizer->get_token_literal();
+ StringName const_id = tokenizer->get_token_literal();
tokenizer->advance();
@@ -4328,22 +4722,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (subexpr->type != Node::TYPE_CONSTANT) {
_set_error("Expected constant expression");
+ return;
}
- const ConstantNode *subexpr_const = static_cast<const ConstantNode *>(subexpr);
+ ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
if (subexpr_const->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum");
+ return;
}
last_assign = subexpr_const->value;
- constant.expression = subexpr;
+ constant.expression = subexpr_const;
} else {
last_assign = last_assign + 1;
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = last_assign;
+ cn->datatype = _type_from_variant(cn->value);
constant.expression = cn;
}
@@ -4353,20 +4750,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (enum_name != "") {
const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression);
- enum_dict[constant.identifier] = cn->value;
+ enum_dict[const_id] = cn->value;
}
- p_class->constant_expressions.push_back(constant);
+ constant.type.has_type = true;
+ constant.type.kind = DataType::BUILTIN;
+ constant.type.builtin_type = Variant::INT;
+ p_class->constant_expressions.insert(const_id, constant);
}
}
if (enum_name != "") {
ClassNode::Constant enum_constant;
- enum_constant.identifier = enum_name;
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = enum_dict;
+ cn->datatype = _type_from_variant(cn->value);
+
enum_constant.expression = cn;
- p_class->constant_expressions.push_back(enum_constant);
+ enum_constant.type = cn->datatype;
+ p_class->constant_expressions.insert(enum_name, enum_constant);
}
if (!_end_statement()) {
@@ -4396,6 +4798,2711 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
+
+ if (p_class->extends_used) {
+ //do inheritance
+ String path = p_class->extends_file;
+
+ Ref<GDScript> script;
+ StringName native;
+ ClassNode *base_class = NULL;
+
+ if (path != "") {
+ //path (and optionally subclasses)
+
+ if (path.is_rel_path()) {
+
+ String base = base_path;
+
+ if (base == "" || base.is_rel_path()) {
+ _set_error("Could not resolve relative path for parent class: " + path, p_class->line);
+ return;
+ }
+ path = base.plus_file(path).simplify_path();
+ }
+ script = ResourceLoader::load(path);
+ if (script.is_null()) {
+ _set_error("Could not load base class: " + path, p_class->line);
+ return;
+ }
+ if (!script->is_valid()) {
+
+ _set_error("Script not fully loaded (cyclic preload?): " + path, p_class->line);
+ return;
+ }
+
+ if (p_class->extends_class.size()) {
+
+ for (int i = 0; i < p_class->extends_class.size(); i++) {
+
+ String sub = p_class->extends_class[i];
+ if (script->get_subclasses().has(sub)) {
+
+ Ref<Script> subclass = script->get_subclasses()[sub]; //avoid reference from disappearing
+ script = subclass;
+ } else {
+
+ _set_error("Could not find subclass: " + sub, p_class->line);
+ return;
+ }
+ }
+ }
+
+ } else {
+
+ if (p_class->extends_class.size() == 0) {
+ _set_error("Parser bug: undecidable inheritance.", p_class->line);
+ ERR_FAIL();
+ }
+ //look around for the subclasses
+
+ int extend_iter = 1;
+ String base = p_class->extends_class[0];
+ ClassNode *p = p_class->owner;
+ Ref<GDScript> base_script;
+
+ if (ScriptServer::is_global_class(base)) {
+ base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base));
+ if (!base_script.is_valid()) {
+ _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line);
+ return;
+ }
+ p = NULL;
+ }
+
+ while (p) {
+
+ bool found = false;
+
+ for (int i = 0; i < p->subclasses.size(); i++) {
+ if (p->subclasses[i]->name == base) {
+ ClassNode *test = p->subclasses[i];
+ while (test) {
+ if (test == p_class) {
+ _set_error("Cyclic inheritance.", test->line);
+ return;
+ }
+ if (test->base_type.kind == DataType::CLASS) {
+ test = test->base_type.class_type;
+ } else {
+ break;
+ }
+ }
+ found = true;
+ if (extend_iter < p_class->extends_class.size()) {
+ // Keep looking at current classes if possible
+ base = p_class->extends_class[extend_iter++];
+ p = p->subclasses[i];
+ } else {
+ base_class = p->subclasses[i];
+ }
+ break;
+ }
+ }
+
+ if (base_class) break;
+ if (found) continue;
+
+ if (p->constant_expressions.has(base)) {
+ if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) {
+ _set_error("Could not resolve constant '" + base + "'.", p_class->line);
+ return;
+ }
+ const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[base].expression);
+ base_script = cn->value;
+ if (base_script.is_null()) {
+ _set_error("Constant is not a class: " + base, p_class->line);
+ return;
+ }
+ break;
+ }
+
+ p = p->owner;
+ }
+
+ if (base_script.is_valid()) {
+
+ String ident = base;
+
+ for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
+
+ String subclass = p_class->extends_class[i];
+
+ ident += ("." + subclass);
+
+ if (base_script->get_subclasses().has(subclass)) {
+
+ base_script = base_script->get_subclasses()[subclass];
+ } else if (base_script->get_constants().has(subclass)) {
+
+ Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
+ if (new_base_class.is_null()) {
+ _set_error("Constant is not a class: " + ident, p_class->line);
+ return;
+ }
+ base_script = new_base_class;
+ } else {
+
+ _set_error("Could not find subclass: " + ident, p_class->line);
+ return;
+ }
+ }
+
+ script = base_script;
+
+ } else if (!base_class) {
+
+ if (p_class->extends_class.size() > 1) {
+
+ _set_error("Invalid inheritance (unknown class + subclasses)", p_class->line);
+ return;
+ }
+ //if not found, try engine classes
+ if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
+
+ _set_error("Unknown class: '" + base + "'", p_class->line);
+ return;
+ }
+
+ native = base;
+ }
+ }
+
+ if (base_class) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::CLASS;
+ p_class->base_type.class_type = base_class;
+ } else if (script.is_valid()) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::GDSCRIPT;
+ p_class->base_type.script_type = script;
+ p_class->base_type.native_type = script->get_instance_base_type();
+ } else if (native != StringName()) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::NATIVE;
+ p_class->base_type.native_type = native;
+ } else {
+ _set_error("Could not determine inheritance", p_class->line);
+ return;
+ }
+
+ } else {
+ // without extends, implicitly extend Reference
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::NATIVE;
+ p_class->base_type.native_type = "Reference";
+ }
+
+ // Recursively determine subclasses
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ _determine_inheritance(p_class->subclasses[i]);
+ }
+}
+
+String GDScriptParser::DataType::to_string() const {
+ if (!has_type) return "var";
+ switch (kind) {
+ case BUILTIN: {
+ if (builtin_type == Variant::NIL) return "null";
+ return Variant::get_type_name(builtin_type);
+ } break;
+ case NATIVE: {
+ if (is_meta_type) {
+ return "GDScriptNativeClass";
+ }
+ return native_type.operator String();
+ } break;
+
+ case GDSCRIPT: {
+ Ref<GDScript> gds = script_type;
+ const String &gds_class = gds->get_script_class_name();
+ if (!gds_class.empty()) {
+ return gds_class;
+ }
+ } // fallthrough
+ case SCRIPT: {
+ if (is_meta_type) {
+ return script_type->get_class_name().operator String();
+ }
+ String name = script_type->get_name();
+ if (name != String()) {
+ return name;
+ }
+ name = script_type->get_path().get_file();
+ if (name != String()) {
+ return name;
+ }
+ return native_type.operator String();
+ } break;
+ case CLASS: {
+ ERR_FAIL_COND_V(!class_type, String());
+ if (is_meta_type) {
+ return "GDScript";
+ }
+ if (class_type->name == StringName()) {
+ return "self";
+ }
+ return class_type->name.operator String();
+ } break;
+ }
+
+ return "Unresolved";
+}
+
+bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
+ tokenizer->advance();
+ r_type.has_type = true;
+
+ bool finished = false;
+ bool can_index = false;
+ String full_name;
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
+ completion_cursor = StringName();
+ completion_type = COMPLETION_TYPE_HINT;
+ completion_class = current_class;
+ completion_function = current_function;
+ completion_line = tokenizer->get_token_line();
+ completion_argument = 0;
+ completion_block = current_block;
+ completion_found = true;
+ completion_ident_is_call = p_can_be_void;
+ tokenizer->advance();
+ }
+
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_PR_VOID: {
+ if (!p_can_be_void) {
+ return false;
+ }
+ r_type.kind = DataType::BUILTIN;
+ r_type.builtin_type = Variant::NIL;
+ } break;
+ case GDScriptTokenizer::TK_BUILT_IN_TYPE: {
+ r_type.builtin_type = tokenizer->get_token_type();
+ if (tokenizer->get_token_type() == Variant::OBJECT) {
+ r_type.kind = DataType::NATIVE;
+ r_type.native_type = "Object";
+ } else {
+ r_type.kind = DataType::BUILTIN;
+ }
+ } break;
+ case GDScriptTokenizer::TK_IDENTIFIER: {
+ r_type.native_type = tokenizer->get_token_identifier();
+ if (ClassDB::class_exists(r_type.native_type) || ClassDB::class_exists("_" + r_type.native_type.operator String())) {
+ r_type.kind = DataType::NATIVE;
+ } else {
+ r_type.kind = DataType::UNRESOLVED;
+ can_index = true;
+ full_name = r_type.native_type;
+ }
+ } break;
+ default: {
+ return false;
+ }
+ }
+
+ tokenizer->advance();
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
+ completion_cursor = r_type.native_type;
+ completion_type = COMPLETION_TYPE_HINT;
+ completion_class = current_class;
+ completion_function = current_function;
+ completion_line = tokenizer->get_token_line();
+ completion_argument = 0;
+ completion_block = current_block;
+ completion_found = true;
+ completion_ident_is_call = p_can_be_void;
+ tokenizer->advance();
+ }
+
+ if (can_index) {
+ while (!finished) {
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_PERIOD: {
+ if (!can_index) {
+ _set_error("Unexpected '.'.");
+ return false;
+ }
+ can_index = false;
+ tokenizer->advance();
+ } break;
+ case GDScriptTokenizer::TK_IDENTIFIER: {
+ if (can_index) {
+ _set_error("Unexpected identifier.");
+ return false;
+ }
+
+ StringName id;
+ bool has_completion = _get_completable_identifier(COMPLETION_TYPE_HINT_INDEX, id);
+ if (id == StringName()) {
+ id = "@temp";
+ }
+
+ full_name += "." + id.operator String();
+ can_index = true;
+ if (has_completion) {
+ completion_cursor = full_name;
+ }
+ } break;
+ default: {
+ finished = true;
+ } break;
+ }
+ }
+
+ if (tokenizer->get_token(-1) == GDScriptTokenizer::TK_PERIOD) {
+ _set_error("Expected subclass identifier.");
+ return false;
+ }
+
+ r_type.native_type = full_name;
+ }
+
+ return true;
+}
+
+GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, int p_line) {
+ if (!p_source.has_type) return p_source;
+ if (p_source.kind != DataType::UNRESOLVED) return p_source;
+
+ Vector<String> full_name = p_source.native_type.operator String().split(".", false);
+ int name_part = 0;
+
+ DataType result;
+ result.has_type = true;
+
+ while (name_part < full_name.size()) {
+
+ bool found = false;
+ StringName id = full_name[name_part];
+ DataType base_type = result;
+
+ ClassNode *p = NULL;
+ if (name_part == 0) {
+ if (ScriptServer::is_global_class(id)) {
+ String script_path = ScriptServer::get_global_class_path(id);
+ if (script_path == self_path) {
+ result.kind = DataType::CLASS;
+ result.class_type = current_class;
+ } else {
+ Ref<Script> script = ResourceLoader::load(script_path);
+ Ref<GDScript> gds = script;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic inheritance).", p_line);
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ } else if (script.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = script;
+ } else {
+ _set_error("Class '" + id + "' was found in global scope but its script could not be loaded.", p_line);
+ return DataType();
+ }
+ }
+ name_part++;
+ continue;
+ } else {
+ p = current_class;
+ }
+ } else if (base_type.kind == DataType::CLASS) {
+ p = base_type.class_type;
+ }
+ while (p) {
+ if (p->constant_expressions.has(id)) {
+ if (p->constant_expressions[id].expression->type != Node::TYPE_CONSTANT) {
+ _set_error("Parser bug: unresolved constant.", p_line);
+ ERR_FAIL_V(result);
+ }
+ const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[id].expression);
+ Ref<GDScript> gds = cn->value;
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ found = true;
+ } else {
+ Ref<Script> scr = cn->value;
+ if (scr.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = scr;
+ found = true;
+ }
+ }
+ break;
+ }
+
+ // Inner classes
+ ClassNode *outer_class = p;
+ while (outer_class) {
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i] == p) {
+ continue;
+ }
+ if (outer_class->subclasses[i]->name == id) {
+ found = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class->subclasses[i];
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ outer_class = outer_class->owner;
+ }
+
+ if (!found && p->base_type.kind == DataType::CLASS) {
+ p = p->base_type.class_type;
+ } else {
+ base_type = p->base_type;
+ break;
+ }
+ }
+
+ // Still look for class constants in parent script
+ if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) {
+ Ref<Script> scr = base_type.script_type;
+ ERR_FAIL_COND_V(scr.is_null(), result);
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+
+ if (constants.has(id)) {
+ Ref<GDScript> gds = constants[id];
+
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ found = true;
+ } else {
+ Ref<Script> scr = constants[id];
+ if (scr.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = scr;
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!found && !for_completion) {
+ String base;
+ if (name_part == 0) {
+ base = "self";
+ } else {
+ base = result.to_string();
+ }
+ _set_error("Identifier '" + String(id) + "' is not a valid type (not a script or class), or could not be found on base '" +
+ base + "'.",
+ p_line);
+ return DataType();
+ }
+
+ name_part++;
+ }
+
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_variant(const Variant &p_value) const {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.kind = DataType::BUILTIN;
+ result.builtin_type = p_value.get_type();
+
+ if (result.builtin_type == Variant::OBJECT) {
+ Object *obj = p_value.operator Object *();
+ if (!obj) {
+ return DataType();
+ }
+ result.native_type = obj->get_class_name();
+ Ref<Script> scr = p_value;
+ if (scr.is_valid()) {
+ result.is_meta_type = true;
+ } else {
+ result.is_meta_type = false;
+ scr = obj->get_script();
+ }
+ if (scr.is_valid()) {
+ result.script_type = scr;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ result.native_type = scr->get_instance_base_type();
+ } else {
+ result.kind = DataType::NATIVE;
+ }
+ }
+
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant) const {
+ DataType ret;
+ if (p_property.type == Variant::NIL && (p_nil_is_variant || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
+ // Variant
+ return ret;
+ }
+ ret.has_type = true;
+ ret.builtin_type = p_property.type;
+ if (p_property.type == Variant::OBJECT) {
+ ret.kind = DataType::NATIVE;
+ ret.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ } else {
+ ret.kind = DataType::BUILTIN;
+ }
+ return ret;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_gdtype(const GDScriptDataType &p_gdtype) const {
+ DataType result;
+ if (!p_gdtype.has_type) {
+ return result;
+ }
+
+ result.has_type = true;
+ result.builtin_type = p_gdtype.builtin_type;
+ result.native_type = p_gdtype.native_type;
+ result.script_type = p_gdtype.script_type;
+
+ switch (p_gdtype.kind) {
+ case GDScriptDataType::BUILTIN: {
+ result.kind = DataType::BUILTIN;
+ } break;
+ case GDScriptDataType::NATIVE: {
+ result.kind = DataType::NATIVE;
+ } break;
+ case GDScriptDataType::GDSCRIPT: {
+ result.kind = DataType::GDSCRIPT;
+ } break;
+ case GDScriptDataType::SCRIPT: {
+ result.kind = DataType::SCRIPT;
+ } break;
+ }
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const {
+ if (!p_a.has_type || !p_b.has_type) {
+ r_valid = true;
+ return DataType();
+ }
+
+ Variant::Type a_type = p_a.kind == DataType::BUILTIN ? p_a.builtin_type : Variant::OBJECT;
+ Variant::Type b_type = p_b.kind == DataType::BUILTIN ? p_b.builtin_type : Variant::OBJECT;
+
+ Variant a;
+ REF a_ref;
+ if (a_type == Variant::OBJECT) {
+ a_ref.instance();
+ a = a_ref;
+ } else {
+ Variant::CallError err;
+ a = Variant::construct(a_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ r_valid = false;
+ return DataType();
+ }
+ }
+ Variant b;
+ REF b_ref;
+ if (b_type == Variant::OBJECT) {
+ b_ref.instance();
+ b = b_ref;
+ } else {
+ Variant::CallError err;
+ b = Variant::construct(b_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ r_valid = false;
+ return DataType();
+ }
+ }
+
+ // Avoid division by zero
+ if (a_type == Variant::INT || a_type == Variant::REAL) {
+ Variant::evaluate(Variant::OP_ADD, a, 1, a, r_valid);
+ }
+ if (b_type == Variant::INT || b_type == Variant::REAL) {
+ Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid);
+ }
+
+ Variant ret;
+ Variant::evaluate(p_op, a, b, ret, r_valid);
+
+ if (r_valid) {
+ return _type_from_variant(ret);
+ }
+
+ return DataType();
+}
+
+Variant::Operator GDScriptParser::_get_variant_operation(const OperatorNode::Operator &p_op) const {
+ switch (p_op) {
+ case OperatorNode::OP_NEG: {
+ return Variant::OP_NEGATE;
+ } break;
+ case OperatorNode::OP_POS: {
+ return Variant::OP_POSITIVE;
+ } break;
+ case OperatorNode::OP_NOT: {
+ return Variant::OP_NOT;
+ } break;
+ case OperatorNode::OP_BIT_INVERT: {
+ return Variant::OP_BIT_NEGATE;
+ } break;
+ case OperatorNode::OP_IN: {
+ return Variant::OP_IN;
+ } break;
+ case OperatorNode::OP_EQUAL: {
+ return Variant::OP_EQUAL;
+ } break;
+ case OperatorNode::OP_NOT_EQUAL: {
+ return Variant::OP_NOT_EQUAL;
+ } break;
+ case OperatorNode::OP_LESS: {
+ return Variant::OP_LESS;
+ } break;
+ case OperatorNode::OP_LESS_EQUAL: {
+ return Variant::OP_LESS_EQUAL;
+ } break;
+ case OperatorNode::OP_GREATER: {
+ return Variant::OP_GREATER;
+ } break;
+ case OperatorNode::OP_GREATER_EQUAL: {
+ return Variant::OP_GREATER_EQUAL;
+ } break;
+ case OperatorNode::OP_AND: {
+ return Variant::OP_AND;
+ } break;
+ case OperatorNode::OP_OR: {
+ return Variant::OP_OR;
+ } break;
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ADD: {
+ return Variant::OP_ADD;
+ } break;
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_SUB: {
+ return Variant::OP_SUBTRACT;
+ } break;
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_MUL: {
+ return Variant::OP_MULTIPLY;
+ } break;
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_DIV: {
+ return Variant::OP_DIVIDE;
+ } break;
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_MOD: {
+ return Variant::OP_MODULE;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_BIT_AND: {
+ return Variant::OP_BIT_AND;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_BIT_OR: {
+ return Variant::OP_BIT_OR;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR:
+ case OperatorNode::OP_BIT_XOR: {
+ return Variant::OP_BIT_XOR;
+ } break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_SHIFT_LEFT: {
+ return Variant::OP_SHIFT_LEFT;
+ }
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_SHIFT_RIGHT: {
+ return Variant::OP_SHIFT_RIGHT;
+ }
+ default: {
+ return Variant::OP_MAX;
+ } break;
+ }
+}
+
+bool GDScriptParser::_is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion) const {
+ // Ignore for completion
+ if (!check_types || for_completion) {
+ return true;
+ }
+ // Can't test if not all have type
+ if (!p_container.has_type || !p_expression.has_type) {
+ return true;
+ }
+
+ // Should never get here unresolved
+ ERR_FAIL_COND_V(p_container.kind == DataType::UNRESOLVED, false);
+ ERR_FAIL_COND_V(p_expression.kind == DataType::UNRESOLVED, false);
+
+ if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
+ bool valid = p_container.builtin_type == p_expression.builtin_type;
+ if (p_allow_implicit_conversion) {
+ valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL);
+ valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT);
+ valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH);
+ valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING);
+ valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL);
+ valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT);
+ valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL);
+ valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL);
+ }
+ return valid;
+ }
+
+ if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) {
+ // Can't mix built-ins with objects
+ return false;
+ }
+
+ // From now on everything is objects, check polymorphism
+ // The container must be the same class or a superclass of the expression
+
+ if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) {
+ // Null can be assigned to object types
+ return true;
+ }
+
+ StringName expr_native;
+ Ref<Script> expr_script;
+ ClassNode *expr_class = NULL;
+
+ switch (p_expression.kind) {
+ case DataType::NATIVE: {
+ if (p_container.kind != DataType::NATIVE) {
+ // Non-native type can't be a superclass of a native type
+ return false;
+ }
+ if (p_expression.is_meta_type) {
+ expr_native = GDScriptNativeClass::get_class_static();
+ } else {
+ expr_native = p_expression.native_type;
+ }
+ } break;
+ case DataType::SCRIPT:
+ case DataType::GDSCRIPT: {
+ if (p_container.kind == DataType::CLASS) {
+ // This cannot be resolved without cyclic dependencies, so just bail out
+ return false;
+ }
+ if (p_expression.is_meta_type) {
+ expr_native = p_expression.script_type->get_class_name();
+ } else {
+ expr_script = p_expression.script_type;
+ expr_native = expr_script->get_instance_base_type();
+ }
+ } break;
+ case DataType::CLASS: {
+ if (p_expression.is_meta_type) {
+ expr_native = GDScript::get_class_static();
+ } else {
+ expr_class = p_expression.class_type;
+ ClassNode *base = expr_class;
+ while (base->base_type.kind == DataType::CLASS) {
+ base = base->base_type.class_type;
+ }
+ expr_native = base->base_type.native_type;
+ expr_script = base->base_type.script_type;
+ }
+ }
+ }
+
+ switch (p_container.kind) {
+ case DataType::NATIVE: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static());
+ } else {
+ return ClassDB::is_parent_class(expr_native, p_container.native_type);
+ }
+ } break;
+ case DataType::SCRIPT:
+ case DataType::GDSCRIPT: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
+ }
+ if (expr_class == head && p_container.script_type->get_path() == self_path) {
+ // Special case: container is self script and expression is self
+ return true;
+ }
+ while (expr_script.is_valid()) {
+ if (expr_script == p_container.script_type) {
+ return true;
+ }
+ expr_script = expr_script->get_base_script();
+ }
+ return false;
+ } break;
+ case DataType::CLASS: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
+ }
+ if (p_container.class_type == head && expr_script.is_valid() && expr_script->get_path() == self_path) {
+ // Special case: container is self and expression is self script
+ return true;
+ }
+ while (expr_class) {
+ if (expr_class == p_container.class_type) {
+ return true;
+ }
+ expr_class = expr_class->base_type.class_type;
+ }
+ return false;
+ }
+ }
+
+ return false;
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
+ if (p_node->get_datatype().has_type) {
+ return p_node->get_datatype();
+ }
+
+ DataType node_type;
+
+ switch (p_node->type) {
+ case Node::TYPE_CONSTANT: {
+ node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value);
+ } break;
+ case Node::TYPE_ARRAY: {
+ node_type.has_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::ARRAY;
+ } break;
+ case Node::TYPE_DICTIONARY: {
+ node_type.has_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::DICTIONARY;
+ } break;
+ case Node::TYPE_SELF: {
+ node_type.has_type = true;
+ node_type.kind = DataType::CLASS;
+ node_type.class_type = current_class;
+ } break;
+ case Node::TYPE_IDENTIFIER: {
+ IdentifierNode *id = static_cast<IdentifierNode *>(p_node);
+ if (id->declared_block) {
+ node_type = id->declared_block->variables[id->name]->get_datatype();
+ } else if (id->name == "#match_value") {
+ // It's a special id just for the match statetement, ignore
+ break;
+ } else if (current_function && current_function->arguments.find(id->name) >= 0) {
+ int idx = current_function->arguments.find(id->name);
+ node_type = current_function->argument_types[idx];
+ } else {
+ node_type = _reduce_identifier_type(NULL, id->name, id->line);
+ }
+ } break;
+ case Node::TYPE_CAST: {
+ CastNode *cn = static_cast<CastNode *>(p_node);
+
+ DataType source_type = _reduce_node_type(cn->source_node);
+ cn->cast_type = _resolve_type(cn->cast_type, cn->line);
+ if (source_type.has_type) {
+
+ bool valid = false;
+ if (check_types) {
+ if (cn->cast_type.kind == DataType::BUILTIN && source_type.kind == DataType::BUILTIN) {
+ valid = Variant::can_convert(source_type.builtin_type, cn->cast_type.builtin_type);
+ }
+ if (cn->cast_type.kind != DataType::BUILTIN && source_type.kind != DataType::BUILTIN) {
+ valid = _is_type_compatible(cn->cast_type, source_type) || _is_type_compatible(source_type, cn->cast_type);
+ }
+
+ if (!valid) {
+ _set_error("Invalid cast. Cannot convert from '" + source_type.to_string() +
+ "' to '" + cn->cast_type.to_string() + "'.",
+ cn->line);
+ return DataType();
+ }
+ }
+ } else {
+ _mark_line_as_unsafe(cn->line);
+ }
+
+ node_type = cn->cast_type;
+
+ } break;
+ case Node::TYPE_OPERATOR: {
+ OperatorNode *op = static_cast<OperatorNode *>(p_node);
+
+ switch (op->op) {
+ case OperatorNode::OP_CALL:
+ case OperatorNode::OP_PARENT_CALL: {
+ node_type = _reduce_function_call_type(op);
+ } break;
+ case OperatorNode::OP_YIELD: {
+ if (op->arguments.size() == 2) {
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ DataType signal_type = _reduce_node_type(op->arguments[1]);
+ // TODO: Check if signal exists when it's a constant
+ if (base_type.has_type && base_type.kind == DataType::BUILTIN && base_type.builtin_type != Variant::NIL && base_type.builtin_type != Variant::OBJECT) {
+ _set_error("First argument of 'yield()' must be an object.", op->line);
+ return DataType();
+ }
+ if (signal_type.has_type && (signal_type.kind != DataType::BUILTIN || signal_type.builtin_type != Variant::STRING)) {
+ _set_error("Second argument of 'yield()' must be a string.", op->line);
+ return DataType();
+ }
+ }
+ // yield can return anything
+ node_type.has_type = false;
+ } break;
+ case OperatorNode::OP_IS: {
+
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: binary operation without 2 arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType value_type = _reduce_node_type(op->arguments[0]);
+ DataType type_type = _reduce_node_type(op->arguments[1]);
+
+ if (check_types && type_type.has_type) {
+ if (!type_type.is_meta_type && (type_type.kind != DataType::NATIVE || !ClassDB::is_parent_class(type_type.native_type, "Script"))) {
+ _set_error("Invalid 'is' test: right operand is not a type (not a native type nor a script).", op->line);
+ return DataType();
+ }
+ type_type.is_meta_type = false; // Test the actual type
+ if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) {
+ // TODO: Make this a warning?
+ _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
+ return DataType();
+ }
+ }
+
+ node_type.has_type = true;
+ node_type.is_constant = true;
+ node_type.is_meta_type = false;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::BOOL;
+ } break;
+ // Unary operators
+ case OperatorNode::OP_NEG:
+ case OperatorNode::OP_POS:
+ case OperatorNode::OP_NOT:
+ case OperatorNode::OP_BIT_INVERT: {
+
+ DataType argument_type = _reduce_node_type(op->arguments[0]);
+ if (!argument_type.has_type) {
+ break;
+ }
+
+ Variant::Operator var_op = _get_variant_operation(op->op);
+ bool valid = false;
+ node_type = _get_operation_type(var_op, argument_type, argument_type, valid);
+
+ if (check_types && !valid) {
+ _set_error("Invalid operand type ('" + argument_type.to_string() +
+ "') to unary operator '" + Variant::get_operator_name(var_op) + "'.",
+ op->line, op->column);
+ return DataType();
+ }
+
+ } break;
+ // Binary operators
+ case OperatorNode::OP_IN:
+ case OperatorNode::OP_EQUAL:
+ case OperatorNode::OP_NOT_EQUAL:
+ case OperatorNode::OP_LESS:
+ case OperatorNode::OP_LESS_EQUAL:
+ case OperatorNode::OP_GREATER:
+ case OperatorNode::OP_GREATER_EQUAL:
+ case OperatorNode::OP_AND:
+ case OperatorNode::OP_OR:
+ case OperatorNode::OP_ADD:
+ case OperatorNode::OP_SUB:
+ case OperatorNode::OP_MUL:
+ case OperatorNode::OP_DIV:
+ case OperatorNode::OP_MOD:
+ case OperatorNode::OP_SHIFT_LEFT:
+ case OperatorNode::OP_SHIFT_RIGHT:
+ case OperatorNode::OP_BIT_AND:
+ case OperatorNode::OP_BIT_OR:
+ case OperatorNode::OP_BIT_XOR: {
+
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: binary operation without 2 arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType argument_a_type = _reduce_node_type(op->arguments[0]);
+ DataType argument_b_type = _reduce_node_type(op->arguments[1]);
+ if (!argument_a_type.has_type || !argument_b_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ Variant::Operator var_op = _get_variant_operation(op->op);
+ bool valid = false;
+ node_type = _get_operation_type(var_op, argument_a_type, argument_b_type, valid);
+
+ if (check_types && !valid) {
+ _set_error("Invalid operand types ('" + argument_a_type.to_string() + "' and '" +
+ argument_b_type.to_string() + "') to operator '" + Variant::get_operator_name(var_op) + "'.",
+ op->line, op->column);
+ return DataType();
+ }
+
+ } break;
+ // Ternary operators
+ case OperatorNode::OP_TERNARY_IF: {
+ if (op->arguments.size() != 3) {
+ _set_error("Parser bug: ternary operation without 3 arguments");
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType true_type = _reduce_node_type(op->arguments[1]);
+ DataType false_type = _reduce_node_type(op->arguments[2]);
+
+ // If types are equal, then the expression is of the same type
+ // If they are compatible, return the broader type
+ if (true_type == false_type || _is_type_compatible(true_type, false_type)) {
+ node_type = true_type;
+ } else if (_is_type_compatible(false_type, true_type)) {
+ node_type = false_type;
+ }
+
+ // TODO: Warn if types aren't compatible
+
+ } break;
+ // Assignment should never happen within an expression
+ case OperatorNode::OP_ASSIGN:
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_ASSIGN_BIT_XOR:
+ case OperatorNode::OP_INIT_ASSIGN: {
+
+ _set_error("Assignment inside expression is not allowed (parser bug?).", op->line);
+ return DataType();
+
+ } break;
+ case OperatorNode::OP_INDEX_NAMED: {
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: named index with invalid arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+ if (op->arguments[1]->type != Node::TYPE_IDENTIFIER) {
+ _set_error("Parser bug: named index without identifier argument.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ IdentifierNode *member_id = static_cast<IdentifierNode *>(op->arguments[1]);
+
+ if (base_type.has_type) {
+ if (check_types && base_type.kind == DataType::BUILTIN) {
+ // Variant type, just test if it's possible
+ DataType result;
+ switch (base_type.builtin_type) {
+ case Variant::NIL:
+ case Variant::DICTIONARY: {
+ result.has_type = false;
+ } break;
+ default: {
+ Variant::CallError err;
+ Variant temp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ bool valid = false;
+ Variant res = temp.get(member_id->name.operator String(), &valid);
+
+ if (valid) {
+ result = _type_from_variant(res);
+ } else if (check_types) {
+ _set_error("Can't get index '" + String(member_id->name.operator String()) + "' on base '" +
+ base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+ } break;
+ }
+ result.is_constant = false;
+ node_type = result;
+ } else {
+ node_type = _reduce_identifier_type(&base_type, member_id->name, op->line);
+ }
+ } else {
+ _mark_line_as_unsafe(op->line);
+ }
+ if (error_set) {
+ return DataType();
+ }
+ } break;
+ case OperatorNode::OP_INDEX: {
+
+ if (op->arguments[1]->type == Node::TYPE_CONSTANT) {
+ ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
+ if (cn->value.get_type() == Variant::STRING) {
+ // Treat this as named indexing
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = cn->value.operator StringName();
+
+ op->op = OperatorNode::OP_INDEX_NAMED;
+ op->arguments[1] = id;
+
+ return _reduce_node_type(op);
+ }
+ }
+
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ DataType index_type = _reduce_node_type(op->arguments[1]);
+
+ if (!base_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ if (check_types && index_type.has_type) {
+ if (base_type.kind == DataType::BUILTIN) {
+ // Check if indexing is valid
+ bool error = index_type.kind != DataType::BUILTIN;
+ if (!error) {
+ switch (base_type.builtin_type) {
+ // Expect int or real as index
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_COLOR_ARRAY:
+ case Variant::POOL_INT_ARRAY:
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::POOL_VECTOR3_ARRAY:
+ case Variant::ARRAY:
+ case Variant::STRING: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::REAL;
+ } break;
+ // Expect String only
+ case Variant::RECT2:
+ case Variant::PLANE:
+ case Variant::QUAT:
+ case Variant::AABB:
+ case Variant::OBJECT: {
+ error = index_type.builtin_type != Variant::STRING;
+ } break;
+ // Expect String or number
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::TRANSFORM2D:
+ case Variant::BASIS:
+ case Variant::TRANSFORM: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::REAL &&
+ index_type.builtin_type != Variant::STRING;
+ } break;
+ // Expect String or int
+ case Variant::COLOR: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
+ } break;
+ }
+ }
+ if (error) {
+ _set_error("Invalid index type (" + index_type.to_string() + ") for base '" + base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+
+ if (op->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
+ // Index is a constant, just try it if possible
+ switch (base_type.builtin_type) {
+ // Arrays/string have variable indexing, can't test directly
+ case Variant::STRING:
+ case Variant::ARRAY:
+ case Variant::DICTIONARY:
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_COLOR_ARRAY:
+ case Variant::POOL_INT_ARRAY:
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::POOL_VECTOR3_ARRAY: {
+ break;
+ }
+ default: {
+ Variant::CallError err;
+ Variant temp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ bool valid = false;
+ Variant res = temp.get(cn->value, &valid);
+
+ if (valid) {
+ node_type = _type_from_variant(res);
+ node_type.is_constant = false;
+ } else if (check_types) {
+ _set_error("Can't get index '" + String(cn->value) + "' on base '" +
+ base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+ } break;
+ }
+ } else {
+ _mark_line_as_unsafe(op->line);
+ }
+ } else if (!for_completion && (index_type.kind != DataType::BUILTIN || index_type.builtin_type != Variant::STRING)) {
+ _set_error("Only strings can be used as index in the base type '" + base_type.to_string() + "'.", op->line);
+ return DataType();
+ }
+ }
+ if (check_types && !node_type.has_type) {
+ // Can infer indexing type for some variant types
+ DataType result;
+ result.has_type = true;
+ result.kind = DataType::BUILTIN;
+ switch (base_type.builtin_type) {
+ // Can't index at all
+ case Variant::NIL:
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::REAL:
+ case Variant::NODE_PATH:
+ case Variant::_RID: {
+ _set_error("Can't index on a value of type '" + base_type.to_string() + "'.", op->line);
+ return DataType();
+ } break;
+ // Return int
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_INT_ARRAY: {
+ result.builtin_type = Variant::INT;
+ } break;
+ // Return real
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::QUAT: {
+ result.builtin_type = Variant::REAL;
+ } break;
+ // Return color
+ case Variant::POOL_COLOR_ARRAY: {
+ result.builtin_type = Variant::COLOR;
+ } break;
+ // Return string
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::STRING: {
+ result.builtin_type = Variant::STRING;
+ } break;
+ // Return Vector2
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::TRANSFORM2D:
+ case Variant::RECT2: {
+ result.builtin_type = Variant::VECTOR2;
+ } break;
+ // Return Vector3
+ case Variant::POOL_VECTOR3_ARRAY:
+ case Variant::AABB:
+ case Variant::BASIS: {
+ result.builtin_type = Variant::VECTOR3;
+ } break;
+ // Depends on the index
+ case Variant::TRANSFORM:
+ case Variant::PLANE:
+ case Variant::COLOR:
+ default: {
+ result.has_type = false;
+ } break;
+ }
+ node_type = result;
+ }
+ } break;
+ default: {
+ _set_error("Parser bug: unhandled operation.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+ }
+ } break;
+ }
+
+ p_node->set_datatype(_resolve_type(node_type, p_node->line));
+ return node_type;
+}
+
+bool GDScriptParser::_get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const {
+
+ r_static = false;
+ r_default_arg_count = 0;
+
+ DataType original_type = p_base_type;
+ ClassNode *base = NULL;
+ FunctionNode *callee = NULL;
+
+ if (p_base_type.kind == DataType::CLASS) {
+ base = p_base_type.class_type;
+ }
+
+ // Look up the current file (parse tree)
+ while (!callee && base) {
+ for (int i = 0; i < base->static_functions.size(); i++) {
+ FunctionNode *func = base->static_functions[i];
+ if (p_function == func->name) {
+ r_static = true;
+ callee = func;
+ break;
+ }
+ }
+ if (!callee && !p_base_type.is_meta_type) {
+ for (int i = 0; i < base->functions.size(); i++) {
+ FunctionNode *func = base->functions[i];
+ if (p_function == func->name) {
+ callee = func;
+ break;
+ }
+ }
+ }
+ p_base_type = base->base_type;
+ if (p_base_type.kind == DataType::CLASS) {
+ base = p_base_type.class_type;
+ } else {
+ break;
+ }
+ }
+
+ if (callee) {
+ r_return_type = callee->get_datatype();
+ for (int i = 0; i < callee->argument_types.size(); i++) {
+ r_arg_types.push_back(callee->argument_types[i]);
+ }
+ r_default_arg_count = callee->default_values.size();
+ return true;
+ }
+
+ // Nothing in current file, check parent script
+ Ref<GDScript> base_gdscript;
+ Ref<Script> base_script;
+ StringName native;
+ if (p_base_type.kind == DataType::GDSCRIPT) {
+ base_gdscript = p_base_type.script_type;
+ } else if (p_base_type.kind == DataType::SCRIPT) {
+ base_script = p_base_type.script_type;
+ } else if (p_base_type.kind == DataType::NATIVE) {
+ native = p_base_type.native_type;
+ }
+
+ while (base_gdscript.is_valid()) {
+ native = base_gdscript->get_instance_base_type();
+
+ Map<StringName, GDScriptFunction *> funcs = base_gdscript->get_member_functions();
+
+ if (funcs.has(p_function)) {
+ GDScriptFunction *f = funcs[p_function];
+ r_static = f->is_static();
+ r_default_arg_count = f->get_default_argument_count();
+ r_return_type = _type_from_gdtype(f->get_return_type());
+ for (int i = 0; i < f->get_argument_count(); i++) {
+ r_arg_types.push_back(_type_from_gdtype(f->get_argument_type(i)));
+ }
+ return true;
+ }
+
+ base_gdscript = base_gdscript->get_base_script();
+ }
+
+ while (base_script.is_valid()) {
+ native = base_script->get_instance_base_type();
+ MethodInfo mi = base_script->get_method_info(p_function);
+
+ if (!(mi == MethodInfo())) {
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_default_arg_count = mi.default_arguments.size();
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ r_arg_types.push_back(_type_from_property(E->get()));
+ }
+ return true;
+ }
+ base_script = base_script->get_base_script();
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ // Only native remains
+ if (!ClassDB::class_exists(native)) {
+ native = "_" + native.operator String();
+ }
+ if (!ClassDB::class_exists(native)) {
+ if (!check_types) return false;
+ ERR_EXPLAIN("Parser bug: Class '" + String(native) + "' not found.");
+ ERR_FAIL_V(false);
+ }
+
+ MethodBind *method = ClassDB::get_method(native, p_function);
+
+ if (!method) {
+ // Try virtual methods
+ List<MethodInfo> virtuals;
+ ClassDB::get_virtual_methods(native, &virtuals);
+
+ for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
+ const MethodInfo &mi = E->get();
+ if (mi.name == p_function) {
+ r_default_arg_count = mi.default_arguments.size();
+ for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
+ r_arg_types.push_back(_type_from_property(pi->get()));
+ }
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_vararg = mi.flags & METHOD_FLAG_VARARG;
+ return true;
+ }
+ }
+
+ // If the base is a script, it might be trying to access members of the Script class itself
+ if (original_type.is_meta_type && !(p_function == "new") && (original_type.kind == DataType::SCRIPT || original_type.kind == DataType::GDSCRIPT)) {
+ method = ClassDB::get_method(original_type.script_type->get_class_name(), p_function);
+
+ if (method) {
+ r_static = true;
+ } else {
+ // Try virtual methods of the script type
+ virtuals.clear();
+ ClassDB::get_virtual_methods(original_type.script_type->get_class_name(), &virtuals);
+ for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
+ const MethodInfo &mi = E->get();
+ if (mi.name == p_function) {
+ r_default_arg_count = mi.default_arguments.size();
+ for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
+ r_arg_types.push_back(_type_from_property(pi->get()));
+ }
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_static = true;
+ r_vararg = mi.flags & METHOD_FLAG_VARARG;
+ return true;
+ }
+ }
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ r_default_arg_count = method->get_default_argument_count();
+ r_return_type = _type_from_property(method->get_return_info(), false);
+ r_vararg = method->is_vararg();
+
+ for (int i = 0; i < method->get_argument_count(); i++) {
+ r_arg_types.push_back(_type_from_property(method->get_argument_info(i)));
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const OperatorNode *p_call) {
+ if (p_call->arguments.size() < 1) {
+ _set_error("Parser bug: function call without enough arguments.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType return_type;
+ List<DataType> arg_types;
+ int default_args_count = 0;
+ int arg_count = p_call->arguments.size();
+ String callee_name;
+ bool is_vararg = false;
+
+ switch (p_call->arguments[0]->type) {
+ case GDScriptParser::Node::TYPE_TYPE: {
+ // Built-in constructor, special case
+ TypeNode *tn = static_cast<TypeNode *>(p_call->arguments[0]);
+
+ Vector<DataType> par_types;
+ par_types.resize(p_call->arguments.size() - 1);
+ for (int i = 1; i < p_call->arguments.size(); i++) {
+ par_types[i - 1] = _reduce_node_type(p_call->arguments[i]);
+ }
+
+ if (error_set) return DataType();
+
+ bool match = false;
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(tn->vtype, &constructors);
+ PropertyInfo return_type;
+
+ for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+
+ if (p_call->arguments.size() - 1 < mi.arguments.size() - mi.default_arguments.size()) {
+ continue;
+ }
+ if (p_call->arguments.size() - 1 > mi.arguments.size()) {
+ continue;
+ }
+
+ bool types_match = true;
+ for (int i = 0; i < par_types.size(); i++) {
+ DataType arg_type;
+ if (mi.arguments[i].type != Variant::NIL) {
+ arg_type.has_type = true;
+ arg_type.kind = mi.arguments[i].type == Variant::OBJECT ? DataType::NATIVE : DataType::BUILTIN;
+ arg_type.builtin_type = mi.arguments[i].type;
+ arg_type.native_type = mi.arguments[i].class_name;
+ }
+
+ if (!_is_type_compatible(arg_type, par_types[i], true)) {
+ types_match = false;
+ break;
+ }
+ }
+
+ if (types_match) {
+ match = true;
+ return_type = mi.return_val;
+ break;
+ }
+ }
+
+ if (match) {
+ return _type_from_property(return_type, false);
+ } else if (check_types) {
+ String err = "No constructor of '";
+ err += Variant::get_type_name(tn->vtype);
+ err += "' matches the signature '";
+ err += Variant::get_type_name(tn->vtype) + "(";
+ for (int i = 0; i < par_types.size(); i++) {
+ if (i > 0) err += ", ";
+ err += par_types[i].to_string();
+ }
+ err += ")'.";
+ _set_error(err, p_call->line, p_call->column);
+ return DataType();
+ }
+ return DataType();
+ } break;
+ case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
+ BuiltInFunctionNode *func = static_cast<BuiltInFunctionNode *>(p_call->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(func->function);
+
+ return_type = _type_from_property(mi.return_val, false);
+
+ // Check arguments
+
+ is_vararg = mi.flags & METHOD_FLAG_VARARG;
+
+ default_args_count = mi.default_arguments.size();
+ callee_name = mi.name;
+ arg_count -= 1;
+
+ // Check each argument type
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ arg_types.push_back(_type_from_property(E->get()));
+ }
+ } break;
+ default: {
+ if (p_call->op == OperatorNode::OP_CALL && p_call->arguments.size() < 2) {
+ _set_error("Parser bug: self method call without enough arguments.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ int arg_id = p_call->op == OperatorNode::OP_CALL ? 1 : 0;
+
+ if (p_call->arguments[arg_id]->type != Node::TYPE_IDENTIFIER) {
+ _set_error("Parser bug: invalid function call argument.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]);
+ callee_name = func_id->name;
+ arg_count -= 1 + arg_id;
+
+ DataType base_type;
+ if (p_call->op == OperatorNode::OP_PARENT_CALL) {
+ base_type = current_class->base_type;
+ } else {
+ base_type = _reduce_node_type(p_call->arguments[0]);
+ }
+
+ if (!base_type.has_type || (base_type.kind == DataType::BUILTIN && base_type.builtin_type == Variant::NIL)) {
+ _mark_line_as_unsafe(p_call->line);
+ return DataType();
+ }
+
+ if (base_type.kind == DataType::BUILTIN) {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ if (check_types) {
+ if (!tmp.has_method(callee_name)) {
+ _set_error("Method '" + callee_name + "' is not declared on base '" + base_type.to_string() + "'.", p_call->line);
+ return DataType();
+ }
+
+ default_args_count = Variant::get_method_default_arguments(base_type.builtin_type, callee_name).size();
+ const Vector<Variant::Type> &var_arg_types = Variant::get_method_argument_types(base_type.builtin_type, callee_name);
+
+ for (int i = 0; i < var_arg_types.size(); i++) {
+ DataType argtype;
+ if (var_arg_types[i] != Variant::NIL) {
+ argtype.has_type = true;
+ argtype.kind = DataType::BUILTIN;
+ argtype.builtin_type = var_arg_types[i];
+ }
+ arg_types.push_back(argtype);
+ }
+ }
+
+ return_type.has_type = true;
+ return_type.kind = DataType::BUILTIN;
+ return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name);
+ break;
+ }
+
+ DataType original_type = base_type;
+ bool is_initializer = callee_name == "new";
+ bool is_static = false;
+ bool valid = false;
+
+ if (is_initializer && original_type.is_meta_type) {
+ // Try to check it as initializer
+ base_type = original_type;
+ callee_name = "_init";
+ base_type.is_meta_type = false;
+
+ valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
+ default_args_count, is_static, is_vararg);
+
+ if (valid) {
+ return_type = original_type;
+ return_type.is_meta_type = false;
+ }
+ }
+
+ if (!valid) {
+ base_type = original_type;
+ return_type = DataType();
+ valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
+ default_args_count, is_static, is_vararg);
+ }
+
+ if (!valid) {
+#ifdef DEBUG_ENABLED
+ if (p_call->arguments[0]->type == Node::TYPE_SELF) {
+ _set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line);
+ return DataType();
+ }
+ _mark_line_as_unsafe(p_call->line);
+#endif
+ return DataType();
+ }
+
+#ifdef DEBUG_ENABLED
+ if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) {
+ if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) {
+ _set_error("Can't call non-static function from a static function.", p_call->line);
+ return DataType();
+ }
+ }
+
+ if (check_types && !is_static && !is_initializer && base_type.is_meta_type) {
+ _set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
+ return DataType();
+ }
+#endif
+ } break;
+ }
+
+ if (!check_types) {
+ return return_type;
+ }
+
+ if (arg_count < arg_types.size() - default_args_count) {
+ _set_error("Too few arguments for '" + callee_name + "()' call. Expected at least " + itos(arg_types.size() - default_args_count) + ".", p_call->line);
+ return return_type;
+ }
+ if (!is_vararg && arg_count > arg_types.size()) {
+ _set_error("Too many arguments for '" + callee_name + "()' call. Expected at most " + itos(arg_types.size()) + ".", p_call->line);
+ return return_type;
+ }
+
+ int arg_diff = p_call->arguments.size() - arg_count;
+ for (int i = arg_diff; i < p_call->arguments.size(); i++) {
+ DataType par_type = _reduce_node_type(p_call->arguments[i]);
+
+ if ((i - arg_diff) >= arg_types.size()) {
+ continue;
+ }
+
+ if (!par_type.has_type) {
+ _mark_line_as_unsafe(p_call->line);
+ } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) {
+ // Supertypes are acceptable for dynamic compliance
+ if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
+ _set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" +
+ par_type.to_string() + ") doesn't match the function argument's type (" +
+ arg_types[i - arg_diff].to_string() + ").",
+ p_call->line);
+ return DataType();
+ } else {
+ _mark_line_as_unsafe(p_call->line);
+ }
+ }
+ }
+
+ return return_type;
+}
+
+bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const {
+ DataType base_type = p_base_type;
+
+ // Check classes in current file
+ ClassNode *base = NULL;
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ }
+
+ while (base) {
+ if (base->constant_expressions.has(p_member)) {
+ r_member_type = base->constant_expressions[p_member].expression->get_datatype();
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ for (int i = 0; i < base->variables.size(); i++) {
+ ClassNode::Member m = base->variables[i];
+ if (m.identifier == p_member) {
+ r_member_type = m.data_type;
+ return true;
+ }
+ }
+ } else {
+ for (int i = 0; i < base->subclasses.size(); i++) {
+ ClassNode *c = base->subclasses[i];
+ if (c->name == p_member) {
+ DataType class_type;
+ class_type.has_type = true;
+ class_type.is_constant = true;
+ class_type.is_meta_type = true;
+ class_type.kind = DataType::CLASS;
+ class_type.class_type = c;
+ r_member_type = class_type;
+ return true;
+ }
+ }
+ }
+
+ base_type = base->base_type;
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ } else {
+ break;
+ }
+ }
+
+ Ref<GDScript> gds;
+ if (base_type.kind == DataType::GDSCRIPT) {
+ gds = base_type.script_type;
+ }
+
+ Ref<Script> scr;
+ if (base_type.kind == DataType::SCRIPT) {
+ scr = base_type.script_type;
+ }
+
+ StringName native;
+ if (base_type.kind == DataType::NATIVE) {
+ native = base_type.native_type;
+ }
+
+ // Check GDScripts
+ while (gds.is_valid()) {
+ if (gds->get_constants().has(p_member)) {
+ Variant c = gds->get_constants()[p_member];
+ r_member_type = _type_from_variant(c);
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ if (gds->get_members().has(p_member)) {
+ r_member_type = _type_from_gdtype(gds->get_member_type(p_member));
+ return true;
+ }
+ }
+
+ native = gds->get_instance_base_type();
+ if (gds->get_base_script().is_valid()) {
+ gds = gds->get_base_script();
+ scr = gds->get_base_script();
+ bool is_meta = base_type.is_meta_type;
+ base_type = _type_from_variant(scr.operator Variant());
+ base_type.is_meta_type = is_meta;
+ } else {
+ break;
+ }
+ }
+
+ // Check other script types
+ while (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ if (constants.has(p_member)) {
+ r_member_type = _type_from_variant(constants[p_member]);
+ return true;
+ }
+
+ List<PropertyInfo> properties;
+ scr->get_script_property_list(&properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ r_member_type = _type_from_property(E->get());
+ return true;
+ }
+ }
+
+ base_type = _type_from_variant(scr.operator Variant());
+ native = scr->get_instance_base_type();
+ scr = scr->get_base_script();
+ }
+
+ // Check ClassDB
+ if (!ClassDB::class_exists(native)) {
+ native = "_" + native.operator String();
+ }
+ if (!ClassDB::class_exists(native)) {
+ if (!check_types) return false;
+ ERR_EXPLAIN("Parser bug: Class '" + String(native) + "' not found.");
+ ERR_FAIL_V(false);
+ }
+
+ bool valid = false;
+ ClassDB::get_integer_constant(native, p_member, &valid);
+ if (valid) {
+ DataType ct;
+ ct.has_type = true;
+ ct.is_constant = true;
+ ct.kind = DataType::BUILTIN;
+ ct.builtin_type = Variant::INT;
+ r_member_type = ct;
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(native, &properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ // Check if a getter exists
+ StringName getter_name = ClassDB::get_property_getter(native, p_member);
+ if (getter_name != StringName()) {
+ // Use the getter return type
+#ifdef DEBUG_METHODS_ENABLED
+ MethodBind *getter_method = ClassDB::get_method(native, getter_name);
+ if (getter_method) {
+ r_member_type = _type_from_property(getter_method->get_return_info());
+ } else {
+ r_member_type = DataType();
+ }
+#else
+ r_member_type = DataType();
+#endif
+ } else {
+ r_member_type = _type_from_property(E->get());
+ }
+ return true;
+ }
+ }
+ }
+
+ // If the base is a script, it might be trying to access members of the Script class itself
+ if (p_base_type.is_meta_type && (p_base_type.kind == DataType::SCRIPT || p_base_type.kind == DataType::GDSCRIPT)) {
+ native = p_base_type.script_type->get_class_name();
+ ClassDB::get_integer_constant(native, p_member, &valid);
+ if (valid) {
+ DataType ct;
+ ct.has_type = true;
+ ct.is_constant = true;
+ ct.kind = DataType::BUILTIN;
+ ct.builtin_type = Variant::INT;
+ r_member_type = ct;
+ return true;
+ }
+
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(native, &properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ // Check if a getter exists
+ StringName getter_name = ClassDB::get_property_getter(native, p_member);
+ if (getter_name != StringName()) {
+ // Use the getter return type
+#ifdef DEBUG_METHODS_ENABLED
+ MethodBind *getter_method = ClassDB::get_method(native, getter_name);
+ if (getter_method) {
+ r_member_type = _type_from_property(getter_method->get_return_info());
+ } else {
+ r_member_type = DataType();
+ }
+#else
+ r_member_type = DataType();
+#endif
+ } else {
+ r_member_type = _type_from_property(E->get());
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line) {
+
+ if (p_base_type && !p_base_type->has_type) {
+ return DataType();
+ }
+
+ DataType base_type;
+
+ // Check classes in current file
+ ClassNode *base = NULL;
+ if (!p_base_type) {
+ // Possibly this is a global, check first
+
+ if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ if (Engine::get_singleton()->has_singleton(p_identifier) || Engine::get_singleton()->has_singleton("_" + p_identifier.operator String())) {
+ result.is_meta_type = false;
+ }
+ result.kind = DataType::NATIVE;
+ result.native_type = p_identifier;
+ return result;
+ }
+
+ ClassNode *outer_class = current_class;
+ while (outer_class) {
+ if (outer_class->name == p_identifier) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class;
+ return result;
+ }
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i] == current_class) {
+ continue;
+ }
+ if (outer_class->subclasses[i]->name == p_identifier) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class->subclasses[i];
+ return result;
+ }
+ }
+ outer_class = outer_class->owner;
+ }
+
+ if (ScriptServer::is_global_class(p_identifier)) {
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ if (scr.is_valid()) {
+ DataType result;
+ result.has_type = true;
+ result.script_type = scr;
+ result.is_meta_type = true;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic inheritance).");
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ return result;
+ }
+ _set_error("Class '" + p_identifier + "' was found in global scope but its script could not be loaded.");
+ return DataType();
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(p_identifier)) {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
+ Variant g = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return _type_from_variant(g);
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
+ Variant g = GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier];
+ return _type_from_variant(g);
+ }
+
+ // Non-tool singletons aren't loaded, check project settings
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ if (name == p_identifier) {
+ String script = ProjectSettings::get_singleton()->get(s);
+ if (script.begins_with("*")) {
+ script = script.right(1);
+ }
+ if (!script.begins_with("res://")) {
+ script = "res://" + script;
+ }
+ Ref<Script> singleton = ResourceLoader::load(script);
+ if (singleton.is_valid()) {
+ DataType result;
+ result.has_type = true;
+ result.script_type = singleton;
+
+ Ref<GDScript> gds = singleton;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Couldn't fully load singleton script '" + p_identifier + "' (possible cyclic reference or parse error).", p_line);
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ }
+ }
+ }
+
+ // Nothing found, keep looking in local scope
+
+ base = current_class;
+ base_type.has_type = true;
+ base_type.is_constant = true;
+ base_type.kind = DataType::CLASS;
+ base_type.class_type = base;
+ } else {
+ base_type = *p_base_type;
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ }
+ }
+
+ DataType member_type;
+
+ if (_get_member_type(base_type, p_identifier, member_type)) {
+ return member_type;
+ }
+
+ if (!p_base_type) {
+ // This means looking in the current class, which type is always known
+ _set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line);
+ }
+
+ _mark_line_as_unsafe(p_line);
+ return DataType();
+}
+
+void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
+
+ _mark_line_as_safe(p_class->line);
+
+ // Constants
+ for (Map<StringName, ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
+ ClassNode::Constant &c = E->get();
+ _mark_line_as_safe(c.expression->line);
+ DataType cont = _resolve_type(c.type, c.expression->line);
+ DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line);
+
+ if (!_is_type_compatible(cont, expr)) {
+ _set_error("Constant value type (" + expr.to_string() + ") is not compatible with declared type (" + cont.to_string() + ").",
+ c.expression->line);
+ return;
+ }
+
+ expr.is_constant = true;
+ c.type = expr;
+ c.expression->set_datatype(expr);
+ }
+
+ // Function declarations
+ for (int i = 0; i < p_class->static_functions.size(); i++) {
+ _check_function_types(p_class->static_functions[i]);
+ if (error_set) return;
+ }
+
+ for (int i = 0; i < p_class->functions.size(); i++) {
+ _check_function_types(p_class->functions[i]);
+ if (error_set) return;
+ }
+
+ // Class variables
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ ClassNode::Member &v = p_class->variables[i];
+
+ DataType tmp;
+ if (_get_member_type(p_class->base_type, v.identifier, tmp)) {
+ _set_error("Member '" + String(v.identifier) + "' already exists in parent class.", v.line);
+ return;
+ }
+
+ _mark_line_as_safe(v.line);
+ v.data_type = _resolve_type(v.data_type, v.line);
+
+ if (v.expression) {
+ DataType expr_type = _reduce_node_type(v.expression);
+
+ if (!_is_type_compatible(v.data_type, expr_type)) {
+ // Try supertype test
+ if (_is_type_compatible(expr_type, v.data_type)) {
+ _mark_line_as_unsafe(v.line);
+ } else {
+ // Try with implicit conversion
+ if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) {
+ _set_error("Assigned expression type (" + expr_type.to_string() + ") doesn't match the variable's type (" +
+ v.data_type.to_string() + ").",
+ v.line);
+ return;
+ }
+
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = v.line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = v.line;
+ tgt_type->value = (int)v.data_type.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = v.line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(v.expression);
+ convert_call->arguments.push_back(tgt_type);
+
+ v.expression = convert_call;
+ v.initial_assignment->arguments[1] = convert_call;
+ }
+ }
+
+ if (v.data_type.infer_type) {
+ if (!expr_type.has_type) {
+ _set_error("Assigned value does not have a set type, variable type cannot be inferred.", v.line);
+ return;
+ }
+ v.data_type = expr_type;
+ v.data_type.is_constant = false;
+ }
+ } else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) {
+ // Create default value based on the type
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->line = v.line;
+ id->name = v.identifier;
+
+ ConstantNode *init = alloc_node<ConstantNode>();
+ init->line = v.line;
+ Variant::CallError err;
+ init->value = Variant::construct(v.data_type.builtin_type, NULL, 0, err);
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->line = v.line;
+ op->op = OperatorNode::OP_INIT_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(init);
+
+ p_class->initializer->statements.push_front(op);
+ v.initial_assignment = op;
+#ifdef DEBUG_ENABLED
+ NewLineNode *nl = alloc_node<NewLineNode>();
+ nl->line = v.line - 1;
+ p_class->initializer->statements.push_front(nl);
+#endif
+ }
+
+ // Check export hint
+ if (v.data_type.has_type && v._export.type != Variant::NIL) {
+ DataType export_type = _type_from_property(v._export);
+ if (!_is_type_compatible(v.data_type, export_type, true)) {
+ _set_error("Export hint type (" + export_type.to_string() + ") doesn't match the variable's type (" +
+ v.data_type.to_string() + ").",
+ v.line);
+ return;
+ }
+ }
+
+ // Setter and getter
+ if (v.setter == StringName() && v.getter == StringName()) continue;
+
+ bool found_getter = false;
+ bool found_setter = false;
+ for (int j = 0; j < p_class->functions.size(); j++) {
+ if (v.setter == p_class->functions[j]->name) {
+ found_setter = true;
+ FunctionNode *setter = p_class->functions[j];
+
+ if (setter->arguments.size() != 1) {
+ _set_error("Setter function needs to receive exactly 1 argument. See '" + setter->name +
+ "()' definition at line " + itos(setter->line) + ".",
+ v.line);
+ return;
+ }
+ if (!_is_type_compatible(v.data_type, setter->argument_types[0])) {
+ _set_error("Setter argument type (" + setter->argument_types[0].to_string() +
+ ") doesn't match the variable's type (" + v.data_type.to_string() + "). See '" +
+ setter->name + "()' definition at line " + itos(setter->line) + ".",
+ v.line);
+ return;
+ }
+ continue;
+ }
+ if (v.getter == p_class->functions[j]->name) {
+ found_getter = true;
+ FunctionNode *getter = p_class->functions[j];
+
+ if (getter->arguments.size() != 0) {
+ _set_error("Getter function can't receive arguments. See '" + getter->name +
+ "()' definition at line " + itos(getter->line) + ".",
+ v.line);
+ return;
+ }
+ if (!_is_type_compatible(v.data_type, getter->get_datatype())) {
+ _set_error("Getter return type (" + getter->get_datatype().to_string() +
+ ") doesn't match the variable's type (" + v.data_type.to_string() +
+ "). See '" + getter->name + "()' definition at line " + itos(getter->line) + ".",
+ v.line);
+ return;
+ }
+ }
+ if (found_getter && found_setter) break;
+ }
+
+ if ((found_getter || v.getter == StringName()) && (found_setter || v.setter == StringName())) continue;
+
+ // Check for static functions
+ for (int j = 0; j < p_class->static_functions.size(); j++) {
+ if (v.setter == p_class->static_functions[j]->name) {
+ FunctionNode *setter = p_class->static_functions[j];
+ _set_error("Setter can't be a static function. See '" + setter->name + "()' definition at line " + itos(setter->line) + ".", v.line);
+ return;
+ }
+ if (v.getter == p_class->static_functions[j]->name) {
+ FunctionNode *getter = p_class->static_functions[j];
+ _set_error("Getter can't be a static function. See '" + getter->name + "()' definition at line " + itos(getter->line) + ".", v.line);
+ return;
+ }
+ }
+
+ if (!found_setter && v.setter != StringName()) {
+ _set_error("Setter function is not defined.", v.line);
+ return;
+ }
+
+ if (!found_getter && v.getter != StringName()) {
+ _set_error("Getter function is not defined.", v.line);
+ return;
+ }
+ }
+
+ // Inner classes
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ current_class = p_class->subclasses[i];
+ _check_class_level_types(current_class);
+ if (error_set) return;
+ current_class = p_class;
+ }
+}
+
+void GDScriptParser::_check_function_types(FunctionNode *p_function) {
+
+ p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
+
+ // Arguments
+ int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
+ for (int i = 0; i < p_function->arguments.size(); i++) {
+
+ // Resolve types
+ p_function->argument_types[i] = _resolve_type(p_function->argument_types[i], p_function->line);
+
+ if (i >= defaults_ofs) {
+ if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) {
+ _set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column);
+ return;
+ }
+
+ OperatorNode *op = static_cast<OperatorNode *>(p_function->default_values[i - defaults_ofs]);
+
+ if (op->op != OperatorNode::OP_ASSIGN || op->arguments.size() != 2) {
+ _set_error("Parser bug: invalid argument default value operation.", p_function->line);
+ return;
+ }
+
+ DataType def_type = _reduce_node_type(op->arguments[1]);
+
+ if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
+ String arg_name = p_function->arguments[i];
+ _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
+ arg_name + "' (" + p_function->arguments[i] + ")",
+ p_function->line);
+ }
+ }
+ }
+
+ if (!(p_function->name == "_init")) {
+ // Signature for the initializer may vary
+ DataType return_type;
+ List<DataType> arg_types;
+ int default_arg_count = 0;
+ bool _static = false;
+ bool vararg = false;
+
+ DataType base_type = current_class->base_type;
+ if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) {
+ bool valid = _static == p_function->_static;
+ valid = valid && return_type == p_function->return_type;
+ valid = valid && p_function->default_values.size() >= default_arg_count;
+ valid = valid && arg_types.size() == p_function->arguments.size();
+ int i = 0;
+ for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) {
+ valid = valid && E->get() == p_function->argument_types[i++];
+ }
+
+ if (!valid) {
+ _set_error("Function signature doesn't match the parent.", p_function->line);
+ return;
+ }
+ }
+ } else {
+ if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
+ _set_error("Constructor cannot return a value.", p_function->line);
+ return;
+ }
+ }
+
+ if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
+ if (!p_function->body->has_return) {
+ _set_error("Non-void function must return a value in all possible paths.", p_function->line);
+ return;
+ }
+ }
+
+ if (p_function->has_yield) {
+ // yield() will make the function return a GDScriptFunctionState, so the type is ambiguous
+ p_function->return_type.has_type = false;
+ }
+}
+
+void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
+
+ // Function blocks
+ for (int i = 0; i < p_class->static_functions.size(); i++) {
+ current_function = p_class->static_functions[i];
+ current_block = current_function->body;
+ _mark_line_as_safe(current_function->line);
+ _check_block_types(current_block);
+ current_block = NULL;
+ current_function = NULL;
+ if (error_set) return;
+ }
+
+ for (int i = 0; i < p_class->functions.size(); i++) {
+ current_function = p_class->functions[i];
+ current_block = current_function->body;
+ _mark_line_as_safe(current_function->line);
+ _check_block_types(current_block);
+ current_block = NULL;
+ current_function = NULL;
+ if (error_set) return;
+ }
+
+ // Inner classes
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ current_class = p_class->subclasses[i];
+ _check_class_blocks_types(current_class);
+ if (error_set) return;
+ current_class = p_class;
+ }
+}
+
+void GDScriptParser::_check_block_types(BlockNode *p_block) {
+
+ Node *last_var_assign = NULL;
+
+ // Check each statement
+ for (List<Node *>::Element *E = p_block->statements.front(); E; E = E->next()) {
+ Node *statement = E->get();
+ switch (statement->type) {
+ case Node::TYPE_NEWLINE:
+ case Node::TYPE_BREAKPOINT:
+ case Node::TYPE_ASSERT: {
+ // Nothing to do
+ } break;
+ case Node::TYPE_LOCAL_VAR: {
+ LocalVarNode *lv = static_cast<LocalVarNode *>(statement);
+ lv->datatype = _resolve_type(lv->datatype, lv->line);
+ _mark_line_as_safe(lv->line);
+
+ if (lv->assign) {
+ DataType assign_type = _reduce_node_type(lv->assign);
+ if (!_is_type_compatible(lv->datatype, assign_type)) {
+ // Try supertype test
+ if (_is_type_compatible(assign_type, lv->datatype)) {
+ _mark_line_as_unsafe(lv->line);
+ } else {
+ // Try implict conversion
+ if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) {
+ _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" +
+ lv->datatype.to_string() + ").",
+ lv->line);
+ return;
+ }
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = lv->line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = lv->line;
+ tgt_type->value = (int)lv->datatype.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = lv->line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(lv->assign);
+ convert_call->arguments.push_back(tgt_type);
+
+ lv->assign = convert_call;
+ lv->assign_op->arguments[1] = convert_call;
+ }
+ }
+ if (lv->datatype.infer_type) {
+ if (!assign_type.has_type) {
+ _set_error("Assigned value does not have a set type, variable type cannot be inferred.", lv->line);
+ return;
+ }
+ lv->datatype = assign_type;
+ lv->datatype.is_constant = false;
+ }
+ if (lv->datatype.has_type && !assign_type.has_type) {
+ _mark_line_as_unsafe(lv->line);
+ }
+ }
+ last_var_assign = lv->assign;
+
+ // TODO: Make a warning
+ /*
+ if (lv->assignments == 0) {
+ _set_error("Variable '" + String(lv->name) + "' is never assigned.", lv->line);
+ return;
+ }
+ */
+ } break;
+ case Node::TYPE_OPERATOR: {
+ OperatorNode *op = static_cast<OperatorNode *>(statement);
+
+ switch (op->op) {
+ case OperatorNode::OP_ASSIGN:
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_ASSIGN_BIT_XOR: {
+ if (op->arguments.size() < 2) {
+ _set_error("Parser bug: operation without enough arguments.", op->line, op->column);
+ return;
+ }
+
+ if (op->arguments[1] == last_var_assign) {
+ // Assignment was already checked
+ break;
+ }
+
+ _mark_line_as_safe(op->line);
+
+ DataType lh_type = _reduce_node_type(op->arguments[0]);
+
+ if (error_set) {
+ return;
+ }
+
+ if (!lh_type.has_type) {
+ if (op->arguments[0]->type == Node::TYPE_OPERATOR) {
+ _mark_line_as_unsafe(op->line);
+ }
+ } else if (lh_type.is_constant) {
+ _set_error("Cannot assign a new value to a constant.", op->line);
+ return;
+ }
+
+ DataType rh_type;
+ if (op->op != OperatorNode::OP_ASSIGN) {
+ // Validate operation
+ DataType arg_type = _reduce_node_type(op->arguments[1]);
+ if (!arg_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ Variant::Operator oper = _get_variant_operation(op->op);
+ bool valid = false;
+ rh_type = _get_operation_type(oper, lh_type, arg_type, valid);
+
+ if (!valid) {
+ _set_error("Invalid operand types ('" + lh_type.to_string() + "' and '" + arg_type.to_string() +
+ "') to assignment operator '" + Variant::get_operator_name(oper) + "'.",
+ op->line);
+ return;
+ }
+ } else {
+ rh_type = _reduce_node_type(op->arguments[1]);
+ }
+
+ if (!_is_type_compatible(lh_type, rh_type)) {
+ // Try supertype test
+ if (_is_type_compatible(rh_type, lh_type)) {
+ _mark_line_as_unsafe(op->line);
+ } else {
+ // Try implict conversion
+ if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) {
+ _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" +
+ lh_type.to_string() + ").",
+ op->line);
+ return;
+ }
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = op->line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = op->line;
+ tgt_type->value = (int)lh_type.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = op->line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(op->arguments[1]);
+ convert_call->arguments.push_back(tgt_type);
+
+ op->arguments[1] = convert_call;
+ }
+ }
+ if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) {
+ _mark_line_as_unsafe(op->line);
+ }
+ } break;
+ case OperatorNode::OP_CALL:
+ case OperatorNode::OP_PARENT_CALL: {
+ _mark_line_as_safe(op->line);
+ _reduce_function_call_type(op);
+ if (error_set) return;
+ } break;
+ default: {
+ _mark_line_as_safe(op->line);
+ _reduce_node_type(op); // Test for safety anyway
+ // TODO: Make this a warning
+ /*_set_error("Standalone expression, nothing is done in this line.", statement->line);
+ return; */
+ }
+ }
+ } break;
+ case Node::TYPE_CONTROL_FLOW: {
+ ControlFlowNode *cf = static_cast<ControlFlowNode *>(statement);
+ _mark_line_as_safe(cf->line);
+
+ switch (cf->cf_type) {
+ case ControlFlowNode::CF_RETURN: {
+ DataType function_type = current_function->get_datatype();
+
+ DataType ret_type;
+ if (cf->arguments.size() > 0) {
+ ret_type = _reduce_node_type(cf->arguments[0]);
+ if (error_set) {
+ return;
+ }
+ }
+
+ if (!function_type.has_type) break;
+
+ if (function_type.kind == DataType::BUILTIN && function_type.builtin_type == Variant::NIL) {
+ // Return void, should not have arguments
+ if (cf->arguments.size() > 0) {
+ _set_error("Void function cannot return a value.", cf->line, cf->column);
+ return;
+ }
+ } else {
+ // Return something, cannot be empty
+ if (cf->arguments.size() == 0) {
+ _set_error("Non-void function must return a value.", cf->line, cf->column);
+ return;
+ }
+
+ if (!_is_type_compatible(function_type, ret_type)) {
+ _set_error("Returned value type (" + ret_type.to_string() + ") doesn't match the function return type (" +
+ function_type.to_string() + ").",
+ cf->line, cf->column);
+ return;
+ }
+ }
+ } break;
+ case ControlFlowNode::CF_MATCH: {
+ MatchNode *match_node = cf->match;
+ _transform_match_statment(match_node);
+ } break;
+ default: {
+ if (cf->body_else) {
+ _mark_line_as_safe(cf->body_else->line);
+ }
+ for (int i = 0; i < cf->arguments.size(); i++) {
+ _reduce_node_type(cf->arguments[i]);
+ }
+ } break;
+ }
+ } break;
+ case Node::TYPE_CONSTANT: {
+ ConstantNode *cn = static_cast<ConstantNode *>(statement);
+ // Strings are fine since they can be multiline comments
+ if (cn->value.get_type() == Variant::STRING) {
+ break;
+ }
+ } // falthrough
+ default: {
+ _mark_line_as_safe(statement->line);
+ _reduce_node_type(statement); // Test for safety anyway
+ // TODO: Make this a warning
+ /* _set_error("Standalone expression, nothing is done in this line.", statement->line);
+ return; */
+ }
+ }
+ }
+
+ // Parse sub blocks
+ for (int i = 0; i < p_block->sub_blocks.size(); i++) {
+ current_block = p_block->sub_blocks[i];
+ _check_block_types(current_block);
+ current_block = p_block;
+ if (error_set) return;
+ }
+}
+
void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) {
if (error_set)
@@ -4440,10 +7547,39 @@ Error GDScriptParser::_parse(const String &p_base_path) {
_set_error("Parse Error: " + tokenizer->get_token_error());
}
+ if (error_set && !for_completion) {
+ return ERR_PARSE_ERROR;
+ }
+
+ _determine_inheritance(main_class);
+
+ if (error_set) {
+ return ERR_PARSE_ERROR;
+ }
+
+ current_class = main_class;
+ current_function = NULL;
+ current_block = NULL;
+#ifdef DEBUG_ENABLED
+ if (for_completion) check_types = false;
+#else
+ check_types = false;
+#endif
+
+ // Resolve all class-level stuff before getting into function blocks
+ _check_class_level_types(main_class);
+
if (error_set) {
+ return ERR_PARSE_ERROR;
+ }
+ // Resolve the function blocks
+ _check_class_blocks_types(main_class);
+
+ if (error_set) {
return ERR_PARSE_ERROR;
}
+
return OK;
}
@@ -4461,7 +7597,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St
return ret;
}
-Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion) {
+Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) {
clear();
@@ -4471,6 +7607,9 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo
validating = p_just_validate;
for_completion = p_for_completion;
+#ifdef DEBUG_ENABLED
+ safe_lines = r_safe_lines;
+#endif // DEBUG_ENABLED
tokenizer = tt;
Error ret = _parse(p_base_path);
memdelete(tt);
@@ -4523,7 +7662,11 @@ void GDScriptParser::clear() {
pending_newline = -1;
parenthesis = 0;
current_export.type = Variant::NIL;
+ check_types = true;
error = "";
+#ifdef DEBUG_ENABLED
+ safe_lines = NULL;
+#endif // DEBUG_ENABLED
}
GDScriptParser::CompletionType GDScriptParser::get_completion_type() {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index b88a59537c..48f256b4c6 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -37,8 +37,68 @@
#include "object.h"
#include "script_language.h"
+struct GDScriptDataType;
+
class GDScriptParser {
public:
+ struct ClassNode;
+
+ struct DataType {
+ enum {
+ BUILTIN,
+ NATIVE,
+ SCRIPT,
+ GDSCRIPT,
+ CLASS,
+ UNRESOLVED
+ } kind;
+
+ bool has_type;
+ bool is_constant;
+ bool is_meta_type; // Whether the value can be used as a type
+ bool infer_type;
+
+ Variant::Type builtin_type;
+ StringName native_type;
+ Ref<Script> script_type;
+ ClassNode *class_type;
+
+ String to_string() const;
+
+ bool operator==(const DataType &other) const {
+ if (!has_type || !other.has_type) {
+ return true; // Can be considered equal for parsing purpose
+ }
+ if (kind != other.kind) {
+ return false;
+ }
+ switch (kind) {
+ case BUILTIN: {
+ return builtin_type == other.builtin_type;
+ } break;
+ case NATIVE: {
+ return native_type == other.native_type;
+ } break;
+ case GDSCRIPT:
+ case SCRIPT: {
+ return script_type == other.script_type;
+ } break;
+ case CLASS: {
+ return class_type == other.class_type;
+ } break;
+ }
+ return false;
+ }
+
+ DataType() :
+ has_type(false),
+ is_constant(false),
+ is_meta_type(false),
+ infer_type(false),
+ builtin_type(Variant::NIL),
+ class_type(NULL) {}
+ };
+
struct Node {
enum Type {
@@ -55,6 +115,7 @@ public:
TYPE_OPERATOR,
TYPE_CONTROL_FLOW,
TYPE_LOCAL_VAR,
+ TYPE_CAST,
TYPE_ASSERT,
TYPE_BREAKPOINT,
TYPE_NEWLINE,
@@ -65,11 +126,17 @@ public:
int column;
Type type;
+ virtual DataType get_datatype() const { return DataType(); }
+ virtual void set_datatype(const DataType &p_datatype) {}
+
virtual ~Node() {}
};
struct FunctionNode;
struct BlockNode;
+ struct ConstantNode;
+ struct LocalVarNode;
+ struct OperatorNode;
struct ClassNode : public Node {
@@ -78,6 +145,7 @@ public:
bool extends_used;
StringName extends_file;
Vector<StringName> extends_class;
+ DataType base_type;
struct Member {
PropertyInfo _export;
@@ -85,15 +153,17 @@ public:
Variant default_value;
#endif
StringName identifier;
+ DataType data_type;
StringName setter;
StringName getter;
int line;
Node *expression;
+ OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode;
};
struct Constant {
- StringName identifier;
Node *expression;
+ DataType type;
};
struct Signal {
@@ -103,7 +173,7 @@ public:
Vector<ClassNode *> subclasses;
Vector<Member> variables;
- Vector<Constant> constant_expressions;
+ Map<StringName, Constant> constant_expressions;
Vector<FunctionNode *> functions;
Vector<FunctionNode *> static_functions;
Vector<Signal> _signals;
@@ -126,15 +196,22 @@ public:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
+ bool has_yield;
StringName name;
+ DataType return_type;
Vector<StringName> arguments;
+ Vector<DataType> argument_types;
Vector<Node *> default_values;
BlockNode *body;
+ virtual DataType get_datatype() const { return return_type; }
+ virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
+
FunctionNode() {
type = TYPE_FUNCTION;
_static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ has_yield = false;
}
};
@@ -142,10 +219,9 @@ public:
ClassNode *parent_class;
BlockNode *parent_block;
- Map<StringName, int> locals;
List<Node *> statements;
- Vector<StringName> variables;
- Vector<int> variable_lines;
+ Map<StringName, LocalVarNode *> variables;
+ bool has_return;
Node *if_condition; //tiny hack to improve code completion on if () blocks
@@ -158,6 +234,7 @@ public:
end_line = -1;
parent_block = NULL;
parent_class = NULL;
+ has_return = false;
}
};
@@ -174,28 +251,53 @@ public:
struct IdentifierNode : public Node {
StringName name;
- IdentifierNode() { type = TYPE_IDENTIFIER; }
+ BlockNode *declared_block; // Simplify lookup by checking if it is declared locally
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ IdentifierNode() {
+ type = TYPE_IDENTIFIER;
+ declared_block = NULL;
+ }
};
struct LocalVarNode : public Node {
StringName name;
Node *assign;
+ OperatorNode *assign_op;
+ int assignments;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
LocalVarNode() {
type = TYPE_LOCAL_VAR;
assign = NULL;
+ assign_op = NULL;
+ assignments = 0;
}
};
struct ConstantNode : public Node {
Variant value;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
ConstantNode() { type = TYPE_CONSTANT; }
};
struct ArrayNode : public Node {
Vector<Node *> elements;
- ArrayNode() { type = TYPE_ARRAY; }
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ ArrayNode() {
+ type = TYPE_ARRAY;
+ datatype.has_type = true;
+ datatype.kind = DataType::BUILTIN;
+ datatype.builtin_type = Variant::ARRAY;
+ }
};
struct DictionaryNode : public Node {
@@ -207,7 +309,15 @@ public:
};
Vector<Pair> elements;
- DictionaryNode() { type = TYPE_DICTIONARY; }
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ DictionaryNode() {
+ type = TYPE_DICTIONARY;
+ datatype.has_type = true;
+ datatype.kind = DataType::BUILTIN;
+ datatype.builtin_type = Variant::DICTIONARY;
+ }
};
struct SelfNode : public Node {
@@ -229,10 +339,6 @@ public:
OP_POS,
OP_NOT,
OP_BIT_INVERT,
- OP_PREINC,
- OP_PREDEC,
- OP_INC,
- OP_DEC,
//binary operators (in precedence order)
OP_IN,
OP_EQUAL,
@@ -273,6 +379,9 @@ public:
Operator op;
Vector<Node *> arguments;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
OperatorNode() { type = TYPE_OPERATOR; }
};
@@ -340,6 +449,15 @@ public:
}
};
+ struct CastNode : public Node {
+ Node *source_node;
+ DataType cast_type;
+ DataType return_type;
+ virtual DataType get_datatype() const { return return_type; }
+ virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
+ CastNode() { type = TYPE_CAST; }
+ };
+
struct AssertNode : public Node {
Node *condition;
AssertNode() { type = TYPE_ASSERT; }
@@ -362,76 +480,6 @@ public:
};
};
- /*
- struct OperatorNode : public Node {
-
- DataType return_cache;
- Operator op;
- Vector<Node*> arguments;
- virtual DataType get_datatype() const { return return_cache; }
-
- OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
- };
-
- struct VariableNode : public Node {
-
- DataType datatype_cache;
- StringName name;
- virtual DataType get_datatype() const { return datatype_cache; }
-
- VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; }
- };
-
- struct ConstantNode : public Node {
-
- DataType datatype;
- Variant value;
- virtual DataType get_datatype() const { return datatype; }
-
- ConstantNode() { type=TYPE_CONSTANT; }
- };
-
- struct BlockNode : public Node {
-
- Map<StringName,DataType> variables;
- List<Node*> statements;
- BlockNode() { type=TYPE_BLOCK; }
- };
-
- struct ControlFlowNode : public Node {
-
- FlowOperation flow_op;
- Vector<Node*> statements;
- ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
- };
-
- struct MemberNode : public Node {
-
- DataType datatype;
- StringName name;
- Node* owner;
- virtual DataType get_datatype() const { return datatype; }
- MemberNode() { type=TYPE_MEMBER; }
- };
-
-
- struct ProgramNode : public Node {
-
- struct Function {
- StringName name;
- FunctionNode*function;
- };
-
- Map<StringName,DataType> builtin_variables;
- Map<StringName,DataType> preexisting_variables;
-
- Vector<Function> functions;
- BlockNode *body;
-
- ProgramNode() { type=TYPE_PROGRAM; }
- };
-*/
-
enum CompletionType {
COMPLETION_NONE,
COMPLETION_BUILT_IN_TYPE_CONSTANT,
@@ -446,6 +494,8 @@ public:
COMPLETION_VIRTUAL_FUNC,
COMPLETION_YIELD,
COMPLETION_ASSIGN,
+ COMPLETION_TYPE_HINT,
+ COMPLETION_TYPE_HINT_INDEX,
};
private:
@@ -463,6 +513,10 @@ private:
String error;
int error_line;
int error_column;
+ bool check_types;
+#ifdef DEBUG_ENABLED
+ Set<int> *safe_lines;
+#endif // DEBUG_ENABLED
int pending_newline;
@@ -507,7 +561,7 @@ private:
PatternNode *_parse_pattern(bool p_static);
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
- void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement);
+ void _transform_match_statment(MatchNode *p_match_statement);
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings);
void _parse_block(BlockNode *p_block, bool p_static);
@@ -515,13 +569,43 @@ private:
void _parse_class(ClassNode *p_class);
bool _end_statement();
+ void _determine_inheritance(ClassNode *p_class);
+ bool _parse_type(DataType &r_type, bool p_can_be_void = false);
+ DataType _resolve_type(const DataType &p_source, int p_line);
+ DataType _type_from_variant(const Variant &p_value) const;
+ DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const;
+ DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const;
+ DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const;
+ Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
+ bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
+ bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
+ bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
+
+ DataType _reduce_node_type(Node *p_node);
+ DataType _reduce_function_call_type(const OperatorNode *p_call);
+ DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line);
+ void _check_class_level_types(ClassNode *p_class);
+ void _check_class_blocks_types(ClassNode *p_class);
+ void _check_function_types(FunctionNode *p_function);
+ void _check_block_types(BlockNode *p_block);
+ _FORCE_INLINE_ void _mark_line_as_safe(int p_line) const {
+#ifdef DEBUG_ENABLED
+ if (safe_lines) safe_lines->insert(p_line);
+#endif // DEBUG_ENABLED
+ }
+ _FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const {
+#ifdef DEBUG_ENABLED
+ if (safe_lines) safe_lines->erase(p_line);
+#endif // DEBUG_ENABLED
+ }
+
Error _parse(const String &p_base_path);
public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
- Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false);
+ Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
bool is_tool_script() const;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 3c8e1ddbe4..940bdcbc8d 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -91,6 +91,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"match",
"func",
"class",
+ "class_name",
"extends",
"is",
"onready",
@@ -100,6 +101,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"setget",
"const",
"var",
+ "as",
+ "void",
"enum",
"preload",
"assert",
@@ -124,6 +127,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"'.'",
"'?'",
"':'",
+ "'->'",
"'$'",
"'\\n'",
"PI",
@@ -187,6 +191,7 @@ static const _kws _keyword_list[] = {
//func
{ GDScriptTokenizer::TK_PR_FUNCTION, "func" },
{ GDScriptTokenizer::TK_PR_CLASS, "class" },
+ { GDScriptTokenizer::TK_PR_CLASS_NAME, "class_name" },
{ GDScriptTokenizer::TK_PR_EXTENDS, "extends" },
{ GDScriptTokenizer::TK_PR_IS, "is" },
{ GDScriptTokenizer::TK_PR_ONREADY, "onready" },
@@ -195,6 +200,8 @@ static const _kws _keyword_list[] = {
{ GDScriptTokenizer::TK_PR_EXPORT, "export" },
{ GDScriptTokenizer::TK_PR_SETGET, "setget" },
{ GDScriptTokenizer::TK_PR_VAR, "var" },
+ { GDScriptTokenizer::TK_PR_AS, "as" },
+ { GDScriptTokenizer::TK_PR_VOID, "void" },
{ GDScriptTokenizer::TK_PR_PRELOAD, "preload" },
{ GDScriptTokenizer::TK_PR_ASSERT, "assert" },
{ GDScriptTokenizer::TK_PR_YIELD, "yield" },
@@ -705,11 +712,9 @@ void GDScriptTokenizerText::_advance() {
if (GETCHAR(1) == '=') {
_make_token(TK_OP_ASSIGN_SUB);
INCPOS(1);
- /*
- } else if (GETCHAR(1)=='-') {
- _make_token(TK_OP_MINUS_MINUS);
+ } else if (GETCHAR(1) == '>') {
+ _make_token(TK_FORWARD_ARROW);
INCPOS(1);
- */
} else {
_make_token(TK_OP_SUB);
}
@@ -1135,9 +1140,9 @@ void GDScriptTokenizerText::advance(int p_amount) {
_advance();
}
- //////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 12
+#define BYTECODE_VERSION 13
Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) {
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index c4f1f9fd94..5bd303224c 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -96,6 +96,7 @@ public:
TK_CF_MATCH,
TK_PR_FUNCTION,
TK_PR_CLASS,
+ TK_PR_CLASS_NAME,
TK_PR_EXTENDS,
TK_PR_IS,
TK_PR_ONREADY,
@@ -105,6 +106,8 @@ public:
TK_PR_SETGET,
TK_PR_CONST,
TK_PR_VAR,
+ TK_PR_AS,
+ TK_PR_VOID,
TK_PR_ENUM,
TK_PR_PRELOAD,
TK_PR_ASSERT,
@@ -130,6 +133,7 @@ public:
TK_QUESTION_MARK,
TK_COLON,
TK_DOLLAR,
+ TK_FORWARD_ARROW,
TK_NEWLINE,
TK_CONST_PI,
TK_CONST_TAU,
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 4b96824dca..fc5972c810 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -231,6 +231,13 @@ void GridMapEditor::_menu_option(int p_option) {
_delete_selection();
} break;
+ case MENU_OPTION_SELECTION_FILL: {
+ if (!selection.active)
+ return;
+
+ _fill_selection();
+
+ } break;
case MENU_OPTION_GRIDMAP_SETTINGS: {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50) * EDSCALE);
} break;
@@ -455,6 +462,29 @@ void GridMapEditor::_delete_selection() {
_validate_selection();
}
+void GridMapEditor::_fill_selection() {
+
+ if (!selection.active)
+ return;
+
+ undo_redo->create_action(TTR("GridMap Fill Selection"));
+ for (int i = selection.begin.x; i <= selection.end.x; i++) {
+
+ for (int j = selection.begin.y; j <= selection.end.y; j++) {
+
+ for (int k = selection.begin.z; k <= selection.end.z; k++) {
+
+ undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_pallete, cursor_rot);
+ undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ }
+ }
+ }
+ undo_redo->commit_action();
+
+ selection.active = false;
+ _validate_selection();
+}
+
void GridMapEditor::_update_duplicate_indicator() {
if (!selection.active || input_action != INPUT_DUPLICATE) {
@@ -1072,6 +1102,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C);
options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X);
+ options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index f79d9aefa0..7c5feda125 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -168,6 +168,7 @@ class GridMapEditor : public VBoxContainer {
MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
MENU_OPTION_SELECTION_DUPLICATE,
MENU_OPTION_SELECTION_CLEAR,
+ MENU_OPTION_SELECTION_FILL,
MENU_OPTION_REMOVE_AREA,
MENU_OPTION_GRIDMAP_SETTINGS
@@ -200,6 +201,7 @@ class GridMapEditor : public VBoxContainer {
void _floor_changed(float p_value);
void _delete_selection();
+ void _fill_selection();
EditorNode *editor;
bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click);
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 0168be3a26..437c0d57fa 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -121,9 +121,7 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
img.instance();
Error err = jpeg_load_image_from_buffer(img.ptr(), p_png, p_size);
- if (err)
- ERR_PRINT("Couldn't initialize ImageLoaderJPG with the given resource.");
-
+ ERR_FAIL_COND_V(err, Ref<Image>());
return img;
}
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 38198c9105..40540a857f 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -11,6 +11,7 @@ if env['builtin_mbedtls']:
"aes.c",
"aesni.c",
"arc4.c",
+ "aria.c",
"asn1parse.c",
"asn1write.c",
"base64.c",
@@ -55,6 +56,7 @@ if env['builtin_mbedtls']:
"pk_wrap.c",
"pkwrite.c",
"platform.c",
+ "platform_util.c",
"ripemd160.c",
"rsa.c",
"rsa_internal.c",
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
index a63e53ec1f..884c26ddfe 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.cpp
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "stream_peer_mbed_tls.h"
+#include "mbedtls/platform_util.h"
+#include "os/file_access.h"
static void my_debug(void *ctx, int level,
const char *file, int line,
@@ -81,6 +83,36 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
return got;
}
+void StreamPeerMbedTLS::_cleanup() {
+
+ mbedtls_ssl_free(&ssl);
+ mbedtls_ssl_config_free(&conf);
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ base = Ref<StreamPeer>();
+ status = STATUS_DISCONNECTED;
+}
+
+Error StreamPeerMbedTLS::_do_handshake() {
+ int ret = 0;
+ while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ERR_PRINTS("TLS handshake error: " + itos(ret));
+ _print_error(ret);
+ disconnect_from_stream();
+ status = STATUS_ERROR;
+ return FAILED;
+ } else if (!blocking_handshake) {
+ // Will retry via poll later
+ return OK;
+ }
+ }
+
+ status = STATUS_CONNECTED;
+ return OK;
+}
+
Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) {
base = p_base;
@@ -95,6 +127,7 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
if (ret != 0) {
ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
+ _cleanup();
return FAILED;
}
@@ -112,29 +145,24 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
mbedtls_ssl_set_bio(&ssl, this, bio_send, bio_recv, NULL);
- while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
- if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- ERR_PRINTS("TLS handshake error: " + itos(ret));
- _print_error(ret);
- status = STATUS_ERROR_HOSTNAME_MISMATCH;
- return FAILED;
- }
- }
+ status = STATUS_HANDSHAKING;
- connected = true;
- status = STATUS_CONNECTED;
+ if ((ret = _do_handshake()) != OK) {
+ status = STATUS_ERROR_HOSTNAME_MISMATCH;
+ return FAILED;
+ }
return OK;
}
Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base) {
- return ERR_UNAVAILABLE;
+ return OK;
}
Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
Error err;
int sent = 0;
@@ -155,7 +183,7 @@ Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
r_sent = 0;
@@ -177,7 +205,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in
Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
Error err;
@@ -199,7 +227,7 @@ Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
r_received = 0;
@@ -218,27 +246,30 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r
void StreamPeerMbedTLS::poll() {
- ERR_FAIL_COND(!connected);
+ ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING);
ERR_FAIL_COND(!base.is_valid());
+ if (status == STATUS_HANDSHAKING) {
+ _do_handshake();
+ return;
+ }
+
int ret = mbedtls_ssl_read(&ssl, NULL, 0);
if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
_print_error(ret);
disconnect_from_stream();
- return;
}
}
int StreamPeerMbedTLS::get_available_bytes() const {
- ERR_FAIL_COND_V(!connected, 0);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
return mbedtls_ssl_get_bytes_avail(&ssl);
}
StreamPeerMbedTLS::StreamPeerMbedTLS() {
- connected = false;
status = STATUS_DISCONNECTED;
}
@@ -248,17 +279,10 @@ StreamPeerMbedTLS::~StreamPeerMbedTLS() {
void StreamPeerMbedTLS::disconnect_from_stream() {
- if (!connected)
+ if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING)
return;
- mbedtls_ssl_free(&ssl);
- mbedtls_ssl_config_free(&conf);
- mbedtls_ctr_drbg_free(&ctr_drbg);
- mbedtls_entropy_free(&entropy);
-
- base = Ref<StreamPeer>();
- connected = false;
- status = STATUS_DISCONNECTED;
+ _cleanup();
}
StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h
index 2b96a194a1..7f4e5a4513 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -48,8 +48,6 @@ private:
Status status;
String hostname;
- bool connected;
-
Ref<StreamPeer> base;
static StreamPeerSSL *_create_func();
@@ -57,9 +55,11 @@ private:
static int bio_recv(void *ctx, unsigned char *buf, size_t len);
static int bio_send(void *ctx, const unsigned char *buf, size_t len);
+ void _cleanup();
protected:
static mbedtls_x509_crt cacert;
+
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
@@ -67,6 +67,8 @@ protected:
static void _bind_methods();
+ Error _do_handshake();
+
public:
virtual void poll();
virtual Error accept_stream(Ref<StreamPeer> p_base);
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index a1dfcf6377..c69a3c9ba6 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -5,9 +5,11 @@ Import('env_modules')
env_mono = env_modules.Clone()
-from compat import byte_to_str
+# TODO move functions to their own modules
def make_cs_files_header(src, dst):
+ from compat import byte_to_str
+
with open(dst, 'w') as header:
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
header.write('#ifndef _CS_FILES_DATA_H\n')
@@ -75,6 +77,13 @@ else:
if ARGUMENTS.get('yolo_copy', False):
env_mono.Append(CPPDEFINES=['YOLO_COPY'])
+# Configure TLS checks
+
+import tls_configure
+conf = Configure(env_mono)
+tls_configure.configure(conf)
+env_mono = conf.Finish()
+
# Build GodotSharpTools solution
@@ -88,7 +97,7 @@ def find_msbuild_unix(filename):
hint_dirs = ['/opt/novell/mono/bin']
if sys.platform == 'darwin':
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
@@ -127,15 +136,25 @@ def find_msbuild_windows():
if not mono_root:
raise RuntimeError('Cannot find mono root directory')
+ framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
+ mono_bin_dir = os.path.join(mono_root, 'bin')
+ msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
+
+ if os.path.isfile(msbuild_mono):
+ # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
+ # building with Mono's MSBuild. They must point to the batch files
+ # in Mono's bin directory to make sure they are executed with Mono.
+ mono_msbuild_env = {
+ 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
+ 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
+ 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
+ }
+ return (msbuild_mono, framework_path, mono_msbuild_env)
+
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
if msbuild_tools_path:
- return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), os.path.join(mono_root, 'lib', 'mono', '4.5'))
- else:
- msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat')
-
- if os.path.isfile(msbuild_mono):
- return (msbuild_mono, '')
+ return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
return None
@@ -145,14 +164,21 @@ def mono_build_solution(source, target, env):
import mono_reg_utils as monoreg
from shutil import copyfile
- framework_path_override = ''
+ framework_path = ''
+
+ msbuild_env = os.environ.copy()
+
+ # Needed when running from Developer Command Prompt for VS
+ if 'PLATFORM' in msbuild_env:
+ del msbuild_env['PLATFORM']
if os.name == 'nt':
msbuild_info = find_msbuild_windows()
if msbuild_info is None:
raise RuntimeError('Cannot find MSBuild executable')
msbuild_path = msbuild_info[0]
- framework_path_override = msbuild_info[1]
+ framework_path = msbuild_info[1]
+ msbuild_env.update(msbuild_info[2])
else:
msbuild_path = find_msbuild_unix('msbuild')
if msbuild_path is None:
@@ -183,14 +209,8 @@ def mono_build_solution(source, target, env):
'/p:Configuration=' + build_config,
]
- if framework_path_override:
- msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override]
-
- msbuild_env = os.environ.copy()
-
- # Needed when running from Developer Command Prompt for VS
- if 'PLATFORM' in msbuild_env:
- del msbuild_env['PLATFORM']
+ if framework_path:
+ msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
try:
subprocess.check_call(msbuild_args, env=msbuild_env)
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 8b52d77f80..9a000a2a72 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -4,23 +4,15 @@ import os
import sys
import subprocess
-from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables
+from distutils.version import LooseVersion
+from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
-def find_file_in_dir(directory, files, prefix='', extension=''):
- if not extension.startswith('.'):
- extension = '.' + extension
- for curfile in files:
- if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
- return curfile
- return ''
-
-
def can_build(env, platform):
- if platform in ["javascript"]:
+ if platform in ['javascript']:
return False # Not yet supported
return True
@@ -30,6 +22,27 @@ def is_enabled():
return False
+def get_doc_classes():
+ return [
+ '@C#',
+ 'CSharpScript',
+ 'GodotSharp',
+ ]
+
+
+def get_doc_path():
+ return 'doc_classes'
+
+
+def find_file_in_dir(directory, files, prefix='', extension=''):
+ if not extension.startswith('.'):
+ extension = '.' + extension
+ for curfile in files:
+ if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
+ return curfile
+ return ''
+
+
def copy_file(src_dir, dst_dir, name):
from shutil import copyfile
@@ -42,13 +55,24 @@ def copy_file(src_dir, dst_dir, name):
copyfile(src_path, dst_path)
+def custom_path_is_dir_create(key, val, env):
+ """Validator to check if Path is a directory, creating it if it does not exist.
+ Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and
+ SCons.Script.File() in order to support the '#' top level directory token.
+ """
+ # Dir constructor will throw an error if the path points to a file
+ fsDir = Dir(val)
+ if not fsDir.exists:
+ os.makedirs(fsDir.abspath)
+
+
def configure(env):
env.use_ptrcall = True
- env.add_module_version_string("mono")
+ env.add_module_version_string('mono')
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
- envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', PathVariable.PathIsDirCreate))
+ envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', custom_path_is_dir_create))
envvars.Update(env)
bits = env['bits']
@@ -73,6 +97,9 @@ def configure(env):
if not mono_root:
raise RuntimeError('Mono installation directory not found')
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -135,7 +162,17 @@ def configure(env):
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
+ # We can't use pkg-config to link mono statically,
+ # but we can still use it to find the mono root directory
+ if not mono_root and mono_static:
+ mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
+ if not mono_root:
+ raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
+
if mono_root:
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -151,18 +188,18 @@ def configure(env):
if mono_static:
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
else:
raise RuntimeError('mono-static: Not supported on this platform')
else:
env.Append(LIBS=[mono_lib])
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LIBS=['iconv', 'pthread'])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
if not mono_static:
@@ -175,14 +212,16 @@ def configure(env):
copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
else:
- if mono_static:
- raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
+ assert not mono_static
+
+ mono_version = pkgconfig_try_find_mono_version()
+ configure_for_mono_version(env, mono_version)
env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
mono_lib_path = ''
mono_so_name = ''
- mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
+ mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -204,13 +243,44 @@ def configure(env):
env.Append(LINKFLAGS='-rdynamic')
-def get_doc_classes():
- return [
- "@C#",
- "CSharpScript",
- "GodotSharp",
- ]
+def configure_for_mono_version(env, mono_version):
+ if mono_version is None:
+ raise RuntimeError('Mono JIT compiler version not found')
+ print('Mono JIT compiler version: ' + str(mono_version))
+ if mono_version >= LooseVersion("5.12.0"):
+ env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
-def get_doc_path():
- return "doc_classes"
+def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
+ tmpenv = Environment()
+ tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
+ return os.path.join(hint_dir, '..')
+ return ''
+
+
+def pkgconfig_try_find_mono_version():
+ lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
+ greater_version = None
+ for line in lines:
+ try:
+ version = LooseVersion(line)
+ if greater_version is None or version > greater_version:
+ greater_version = version
+ except ValueError:
+ pass
+ return greater_version
+
+
+def mono_root_try_find_mono_version(mono_root):
+ from compat import decode_utf8
+
+ output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version'])
+ first_line = decode_utf8(output.splitlines()[0])
+ try:
+ return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
+ except (ValueError, IndexError):
+ return None
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 46c40b2690..996e73a4bb 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -49,6 +49,8 @@
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h"
+#include "utils/macros.h"
+#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -119,7 +121,7 @@ void CSharpLanguage::init() {
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&gdsharp_editor_init_callback);
- GLOBAL_DEF("mono/export/include_scripts_content", true);
+ GLOBAL_DEF("mono/export/include_scripts_content", false);
#endif
}
@@ -296,30 +298,28 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
String script_template = "using " BINDINGS_NAMESPACE ";\n"
"using System;\n"
"\n"
- "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n"
+ "public class %CLASS% : %BASE%\n"
"{\n"
- " // Member variables here, example:\n"
+ " // Declare member variables here. Examples:\n"
" // private int a = 2;\n"
- " // private string b = \"textvar\";\n"
+ " // private string b = \"text\";\n"
"\n"
+ " // Called when the node enters the scene tree for the first time.\n"
" public override void _Ready()\n"
" {\n"
- " // Called every time the node is added to the scene.\n"
- " // Initialization here.\n"
" \n"
" }\n"
"\n"
- "// public override void _Process(float delta)\n"
- "// {\n"
- "// // Called every frame. Delta is time since last frame.\n"
- "// // Update game logic here.\n"
- "// \n"
- "// }\n"
+ "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n"
+ "// public override void _Process(float delta)\n"
+ "// {\n"
+ "// \n"
+ "// }\n"
"}\n";
String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
- script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name)
- .replace("%CLASS_NAME%", p_class_name);
+ script_template = script_template.replace("%BASE%", base_class_name)
+ .replace("%CLASS%", p_class_name);
Ref<CSharpScript> script;
script.instance();
@@ -476,7 +476,7 @@ String CSharpLanguage::_get_indentation() const {
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED
- // Printing an error here will result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
@@ -500,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- // Printing an error here could result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
- MonoArray *frames = st_get_frames(p_stack_trace, &exc);
+ MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -529,10 +529,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
- get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc);
+ get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -561,12 +561,12 @@ void CSharpLanguage::frame() {
ERR_FAIL_NULL(thunk);
- MonoObject *ex;
- thunk(task_scheduler, &ex);
+ MonoException *exc = NULL;
+ thunk(task_scheduler, (MonoObject **)&exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL();
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
+ _UNREACHABLE_();
}
}
}
@@ -745,10 +745,9 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> scr = E->key();
- scr->signals_invalidated = true;
scr->exports_invalidated = true;
+ scr->signals_invalidated = true;
scr->reload(p_soft_reload);
- scr->update_signals();
scr->update_exports();
//restore state if saved
@@ -1078,11 +1077,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *value = property->get_value(mono_object, &exc);
if (exc) {
r_ret = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
} else {
r_ret = GDMonoMarshal::mono_object_to_variant(value);
}
@@ -1490,12 +1489,12 @@ bool CSharpScript::_update_exports() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- MonoObject *ex = NULL;
- ctor->invoke(tmp_object, NULL, &ex);
+ MonoException *exc = NULL;
+ ctor->invoke(tmp_object, NULL, &exc);
- if (ex) {
+ if (exc) {
ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
- mono_print_unhandled_exception(ex);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
tmp_object = NULL;
ERR_FAIL_V(false);
}
@@ -1544,11 +1543,11 @@ bool CSharpScript::_update_exports() {
exported_members_cache.push_front(prop_info);
if (tmp_object) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *ret = property->get_value(tmp_object, &exc);
if (exc) {
exported_members_defval_cache[name] = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
} else {
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
}
@@ -1579,42 +1578,38 @@ bool CSharpScript::_update_exports() {
return false;
}
-bool CSharpScript::_update_signals() {
- if (!valid)
- return false;
-
- bool changed = false;
-
- if (signals_invalidated) {
- signals_invalidated = false;
+void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- GDMonoClass *top = script_class;
+ // no need to load the script's signals more than once
+ if (!signals_invalidated) {
+ return;
+ }
- _signals.clear();
- changed = true; // TODO Do a real check for change
+ // make sure this classes signals are empty when loading for the first time
+ _signals.clear();
- while (top && top != native) {
- const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
- for (int i = delegates.size() - 1; i >= 0; --i) {
- Vector<Argument> parameters;
+ GDMonoClass *top = p_class;
+ while (top && top != p_native_class) {
+ const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
+ for (int i = delegates.size() - 1; i >= 0; --i) {
+ Vector<Argument> parameters;
- GDMonoClass *delegate = delegates[i];
+ GDMonoClass *delegate = delegates[i];
- if (_get_signal(top, delegate, parameters)) {
- _signals[delegate->get_name()] = parameters;
- }
+ if (_get_signal(top, delegate, parameters)) {
+ _signals[delegate->get_name()] = parameters;
}
-
- top = top->get_parent_class();
}
+
+ top = top->get_parent_class();
}
- return changed;
+ signals_invalidated = false;
}
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate);
+ MonoType *raw_type = p_delegate->get_mono_type();
if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
// Arguments are accessibles as arguments of .Invoke method
@@ -1848,6 +1843,8 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) {
top = top->get_parent_class();
}
+ script->load_script_signals(script->script_class, script->native);
+
return script;
}
@@ -1918,7 +1915,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Construct
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- ctor->invoke(mono_object, p_args, NULL);
+ ctor->invoke(mono_object, p_args);
// Tie managed to unmanaged
instance->gchandle = MonoGCHandle::create_strong(mono_object);
@@ -1973,7 +1970,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
_update_exports();
- _update_signals();
return si;
#else
return NULL;
@@ -1992,8 +1988,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
ERR_FAIL_V(NULL);
}
- update_signals();
-
if (native) {
String native_name = native->get_name();
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -2114,6 +2108,8 @@ Error CSharpScript::reload(bool p_keep_state) {
top->fetch_methods_with_godot_api_checks(native);
top = top->get_parent_class();
}
+
+ load_script_signals(script_class, native);
}
return OK;
@@ -2173,10 +2169,6 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
}
-void CSharpScript::update_signals() {
- _update_signals();
-}
-
Ref<Script> CSharpScript::get_base_script() const {
// TODO search in metadata file once we have it, not important any way?
@@ -2241,9 +2233,10 @@ CSharpScript::CSharpScript() :
#ifdef TOOLS_ENABLED
source_changed_cache = false;
exports_invalidated = true;
- signals_invalidated = true;
#endif
+ signals_invalidated = true;
+
_resource_path_changed();
#ifdef DEBUG_ENABLED
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index cae2bbf40a..7f9732c297 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -111,7 +111,7 @@ class CSharpScript : public Script {
void _clear();
- bool _update_signals();
+ void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
bool _update_exports();
@@ -149,7 +149,6 @@ public:
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
- virtual void update_signals();
/* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
@@ -293,7 +292,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
+ /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines = NULL) const { return true; }
virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 4c598d4f37..307a7d3e94 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -100,8 +100,6 @@
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
-#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
@@ -1338,7 +1336,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.empty()) {
p_output.push_back("return " + im_call + ";\n");
} else {
- p_output.push_back(INDENT3);
p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
p_output.push_back("\n");
}
@@ -1768,6 +1765,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
continue;
}
+ if (!ClassDB::is_class_enabled(type_cname)) {
+ if (verbose_output)
+ WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled");
+ class_list.pop_front();
+ continue;
+ }
+
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname);
TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type);
@@ -2337,7 +2341,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
- INSERT_ARRAY(Array, object);
INSERT_ARRAY(PoolIntArray, int);
INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
@@ -2355,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#undef INSERT_ARRAY
+ // Array
+ itype = TypeInterface();
+ itype.name = "Array";
+ itype.cname = itype.name;
+ itype.proxy_name = "Array";
+ itype.c_out = "\treturn memnew(Array(%1));\n";
+ itype.c_type = itype.name;
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
+ itype.cs_type = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Array(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
+ builtin_types.insert(itype.cname, itype);
+
// Dictionary
itype = TypeInterface();
itype.name = "Dictionary";
itype.cname = itype.name;
- itype.proxy_name = "Dictionary<object, object>";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n";
- itype.c_arg_in = "&%s_in";
+ itype.proxy_name = "Dictionary";
+ itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Dictionary(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index e4269b0aec..bc95607743 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
Variant dir = p_dir;
Variant compile_items = p_files;
const Variant *args[2] = { &dir, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
Variant core_dll_path = p_core_dll_path;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
Variant name = p_name;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &name, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
Variant item_type = p_item_type;
Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include };
- MonoObject *ex = NULL;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 43d58caad2..b3b259e851 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -64,6 +64,7 @@ String _find_build_engine_on_unix(const String &p_name) {
const char *locations[] = {
#ifdef OSX_ENABLED
"/Library/Frameworks/Mono.framework/Versions/Current/bin/",
+ "/usr/local/var/homebrew/linked/mono/bin/",
#endif
"/opt/novell/mono/bin/"
};
@@ -512,14 +513,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *ctor_args[2] = { &solution, &config };
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
- ctor->invoke(mono_object, ctor_args, &ex);
+ ctor->invoke(mono_object, ctor_args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
@@ -534,14 +535,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
- ex = NULL;
+ exc = NULL;
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
- build_method->invoke(mono_object, args, &ex);
+ build_method->invoke(mono_object, args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 998da8bda3..faeb58e5a7 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -278,13 +278,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
about_label->set_autowrap(true);
String about_text =
String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
- "It is at the alpha stage and thus not suitable for use in production.\n\n" +
- "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " +
+ "It is currently in an alpha stage and is not suitable for use in production.\n\n" +
+ "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " +
+ "Bugs and usability issues will be addressed gradually over future 3.x releases, " +
"including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
- "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " +
- "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" +
- "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" +
+ "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
about_label->set_text(about_text);
diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp
index 48a285561d..9f05711fd6 100644
--- a/modules/mono/editor/monodevelop_instance.cpp
+++ b/modules/mono/editor/monodevelop_instance.cpp
@@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
ERR_FAIL_NULL(execute_method);
ERR_FAIL_COND(gc_handle.is_null());
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant files = p_files;
const Variant *args[1] = { &files };
- execute_method->invoke(gc_handle->get_target(), args, &ex);
+ execute_method->invoke(gc_handle->get_target(), args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
@@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
GDMonoMethod *ctor = klass->get_method(".ctor", 1);
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant solution = p_solution;
const Variant *args[1] = { &solution };
- ctor->invoke(obj, args, &ex);
+ ctor->invoke(obj, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
new file mode 100644
index 0000000000..0551c1991a
--- /dev/null
+++ b/modules/mono/glue/collections_glue.cpp
@@ -0,0 +1,240 @@
+/*************************************************************************/
+/* collections_glue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "collections_glue.h"
+
+#include <mono/metadata/exception.h>
+
+#include "../mono_gd/gd_mono_class.h"
+
+Array *godot_icall_Array_Ctor() {
+ return memnew(Array);
+}
+
+void godot_icall_Array_Dtor(Array *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
+}
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+int godot_icall_Array_Count(Array *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+ ptr->append(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Clear(Array *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
+}
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
+ int count = ptr->size();
+
+ if (mono_array_length(array) < (array_index + count)) {
+ MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ GDMonoUtils::set_pending_exception(exc);
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
+ mono_array_setref(array, array_index, boxed);
+ array_index++;
+ }
+}
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
+}
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
+ int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+ if (idx >= 0) {
+ ptr->remove(idx);
+ return true;
+ }
+ return false;
+}
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->remove(index);
+}
+
+Dictionary *godot_icall_Dictionary_Ctor() {
+ return memnew(Dictionary);
+}
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!exc);
+#endif
+ GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::set_pending_exception((MonoException *)exc);
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ret);
+}
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
+ return memnew(Array(ptr->keys()));
+}
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
+ return memnew(Array(ptr->values()));
+}
+
+int godot_icall_Dictionary_Count(Dictionary *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
+ return;
+ }
+ ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ // no dupes
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value);
+}
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->erase_checked(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+
+ // no dupes
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
+ ptr->erase_checked(varKey);
+ return true;
+ }
+
+ return false;
+}
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ *value = NULL;
+ return false;
+ }
+ *value = GDMonoMarshal::variant_to_mono_object(ret);
+ return true;
+}
+
+void godot_register_collections_icalls() {
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
+
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
+}
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
new file mode 100644
index 0000000000..eb5ecfb725
--- /dev/null
+++ b/modules/mono/glue/collections_glue.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* collections_glue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef COLLECTIONS_GLUE_H
+#define COLLECTIONS_GLUE_H
+
+#include "core/array.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+// Array
+
+Array *godot_icall_Array_Ctor();
+
+void godot_icall_Array_Dtor(Array *ptr);
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index);
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
+
+int godot_icall_Array_Count(Array *ptr);
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Clear(Array *ptr);
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index);
+
+// Dictionary
+
+Dictionary *godot_icall_Dictionary_Ctor();
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr);
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key);
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr);
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr);
+
+int godot_icall_Dictionary_Count(Dictionary *ptr);
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr);
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
+
+// Register internal calls
+
+void godot_register_collections_icalls();
+
+#endif // COLLECTIONS_GLUE_H
diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs
new file mode 100644
index 0000000000..51f57daef4
--- /dev/null
+++ b/modules/mono/glue/cs_files/Array.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ class ArraySafeHandle : SafeHandle
+ {
+ public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Array.godot_icall_Array_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Add(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
+
+ ArraySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Array()
+ {
+ safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ safeHandle = new ArraySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ return godot_icall_Array_At(GetPtr(), index);
+ }
+ set
+ {
+ godot_icall_Array_SetAt(GetPtr(), index, value);
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Array_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object item)
+ {
+ godot_icall_Array_Add(GetPtr(), item);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Array_Clear(GetPtr());
+ }
+
+ public bool Contains(object item)
+ {
+ return godot_icall_Array_Contains(GetPtr(), item);
+ }
+
+ public void CopyTo(object[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // Internal call may throw ArgumentException
+ godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex);
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return godot_icall_Array_At(GetPtr(), i);
+ }
+ }
+
+ public int IndexOf(object item)
+ {
+ return godot_icall_Array_IndexOf(GetPtr(), item);
+ }
+
+ public void Insert(int index, object item)
+ {
+ godot_icall_Array_Insert(GetPtr(), index, item);
+ }
+
+ public bool Remove(object item)
+ {
+ return godot_icall_Array_Remove(GetPtr(), item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ godot_icall_Array_RemoveAt(GetPtr(), index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ {
+ Array objectArray;
+
+ public Array()
+ {
+ objectArray = new Array();
+ }
+
+ public Array(Array array)
+ {
+ objectArray = array;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ public static explicit operator Array(Array<T> from)
+ {
+ return from.objectArray;
+ }
+
+ public T this[int index]
+ {
+ get
+ {
+ return (T)objectArray[index];
+ }
+ set
+ {
+ objectArray[index] = value;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectArray.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectArray.IsReadOnly;
+ }
+ }
+
+ public void Add(T item)
+ {
+ objectArray.Add(item);
+ }
+
+ public void Clear()
+ {
+ objectArray.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return objectArray.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // TODO This may be quite slow because every element access is an internal call.
+ // It could be moved entirely to an internal call if we find out how to do the cast there.
+
+ int count = objectArray.Count;
+
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = (T)objectArray[i];
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ int count = objectArray.Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return (T)objectArray[i];
+ }
+ }
+
+ public int IndexOf(T item)
+ {
+ return objectArray.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ objectArray.Insert(index, item);
+ }
+
+ public bool Remove(T item)
+ {
+ return objectArray.Remove(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ objectArray.RemoveAt(index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index aa49a5e04f..c280d32c61 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -343,17 +343,17 @@ namespace Godot
{
var tr = this;
- real_t temp = this[0, 1];
- this[0, 1] = this[1, 0];
- this[1, 0] = temp;
+ real_t temp = tr[0, 1];
+ tr[0, 1] = tr[1, 0];
+ tr[1, 0] = temp;
- temp = this[0, 2];
- this[0, 2] = this[2, 0];
- this[2, 0] = temp;
+ temp = tr[0, 2];
+ tr[0, 2] = tr[2, 0];
+ tr[2, 0] = temp;
- temp = this[1, 2];
- this[1, 2] = this[2, 1];
- this[2, 1] = temp;
+ temp = tr[1, 2];
+ tr[1, 2] = tr[2, 1];
+ tr[2, 1] = temp;
return tr;
}
diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs
new file mode 100644
index 0000000000..57a1960ad9
--- /dev/null
+++ b/modules/mono/glue/cs_files/Dictionary.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ class DictionarySafeHandle : SafeHandle
+ {
+ public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Dictionary.godot_icall_Dictionary_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Dictionary :
+ IDictionary<object, object>,
+ ICollection<KeyValuePair<object, object>>,
+ IEnumerable<KeyValuePair<object, object>>,
+ IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
+
+ DictionarySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Dictionary()
+ {
+ safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ safeHandle = new DictionarySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[object key]
+ {
+ get
+ {
+ return godot_icall_Dictionary_GetValue(GetPtr(), key);
+ }
+ set
+ {
+ godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ }
+ }
+
+ public ICollection<object> Keys
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<object> Values
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Dictionary_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object key, object value)
+ {
+ godot_icall_Dictionary_Add(GetPtr(), key, value);
+ }
+
+ public void Add(KeyValuePair<object, object> item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Dictionary_Clear(GetPtr());
+ }
+
+ public bool Contains(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool ContainsKey(object key)
+ {
+ return godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ }
+
+ public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<object, object>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(object key)
+ {
+ return godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ }
+
+ public bool Remove(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool TryGetValue(object key, out object value)
+ {
+ object retValue;
+ bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue);
+ value = found ? retValue : default(object);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+
+ public class Dictionary<TKey, TValue> :
+ IDictionary<TKey, TValue>,
+ ICollection<KeyValuePair<TKey, TValue>>,
+ IEnumerable<KeyValuePair<TKey, TValue>>
+ {
+ Dictionary objectDict;
+
+ public Dictionary()
+ {
+ objectDict = new Dictionary();
+ }
+
+ public Dictionary(Dictionary dictionary)
+ {
+ objectDict = dictionary;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
+ {
+ return from.objectDict;
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return (TValue)objectDict[key];
+ }
+ set
+ {
+ objectDict[key] = value;
+ }
+ }
+
+ public ICollection<TKey> Keys
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
+ return new Array<TKey>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<TValue> Values
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
+ return new Array<TValue>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectDict.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectDict.IsReadOnly;
+ }
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ objectDict.Add(key, value);
+ }
+
+ public void Add(KeyValuePair<TKey, TValue> item)
+ {
+ objectDict.Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ objectDict.Clear();
+ }
+
+ public bool Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return objectDict.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(TKey key)
+ {
+ return objectDict.Remove(key);
+ }
+
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ object retValue;
+ bool found = objectDict.TryGetValue(key, out retValue);
+ value = found ? (TValue)retValue : default(TValue);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs
index ec1534cb9a..e2457ff98b 100644
--- a/modules/mono/glue/cs_files/GD.cs
+++ b/modules/mono/glue/cs_files/GD.cs
@@ -1,4 +1,9 @@
using System;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
// TODO: Add comments describing what this class does. It is not obvious.
@@ -16,22 +21,22 @@ namespace Godot
return NativeCalls.godot_icall_Godot_convert(what, type);
}
- public static float Db2Linear(float db)
+ public static real_t Db2Linear(real_t db)
{
- return (float)Math.Exp(db * 0.11512925464970228420089957273422);
+ return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
- public static float Dectime(float value, float amount, float step)
+ public static real_t DecTime(real_t value, real_t amount, real_t step)
{
- float sgn = value < 0 ? -1.0f : 1.0f;
- float val = Mathf.Abs(value);
+ real_t sgn = Mathf.Sign(value);
+ real_t val = Mathf.Abs(value);
val -= amount * step;
- if (val < 0.0f)
- val = 0.0f;
+ if (val < 0)
+ val = 0;
return val * sgn;
}
- public static FuncRef Funcref(Object instance, string funcname)
+ public static FuncRef FuncRef(Object instance, string funcname)
{
var ret = new FuncRef();
ret.SetInstance(instance);
@@ -49,9 +54,9 @@ namespace Godot
return NativeCalls.godot_icall_Godot_instance_from_id(instanceId);
}
- public static double Linear2Db(double linear)
+ public static real_t Linear2Db(real_t linear)
{
- return Math.Log(linear) * 8.6858896380650365530225783783321;
+ return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
}
public static Resource Load(string path)
@@ -69,22 +74,22 @@ namespace Godot
Print(System.Environment.StackTrace);
}
- public static void Printerr(params object[] what)
+ public static void PrintErr(params object[] what)
{
NativeCalls.godot_icall_Godot_printerr(what);
}
- public static void Printraw(params object[] what)
+ public static void PrintRaw(params object[] what)
{
NativeCalls.godot_icall_Godot_printraw(what);
}
- public static void Prints(params object[] what)
+ public static void PrintS(params object[] what)
{
NativeCalls.godot_icall_Godot_prints(what);
}
- public static void Printt(params object[] what)
+ public static void PrintT(params object[] what)
{
NativeCalls.godot_icall_Godot_printt(what);
}
@@ -183,7 +188,7 @@ namespace Godot
return NativeCalls.godot_icall_Godot_var2str(var);
}
- public static WeakRef Weakref(Object obj)
+ public static WeakRef WeakRef(Object obj)
{
return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj));
}
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index ff4477cc6c..6ad4b3dcb2 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -1,36 +1,17 @@
using System;
-using System.Collections.Generic;
namespace Godot
{
- internal static class MarshalUtils
+ static class MarshalUtils
{
- private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
+ static bool IsArrayGenericType(Type type)
{
- var ret = new Dictionary<object, object>();
-
- for (int i = 0; i < keys.Length; i++)
- {
- ret.Add(keys[i], values[i]);
- }
-
- return ret;
- }
-
- private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
- {
- var keys = from.Keys;
- keysTo = new object[keys.Count];
- keys.CopyTo(keysTo, 0);
-
- var values = from.Values;
- valuesTo = new object[values.Count];
- values.CopyTo(valuesTo, 0);
+ return type.GetGenericTypeDefinition() == typeof(Array<>);
}
- private static Type GetDictionaryType()
+ static bool IsDictionaryGenericType(Type type)
{
- return typeof(Dictionary<object, object>);
+ return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
}
}
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index 0d20a12563..a89dfe5f27 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -138,19 +138,9 @@ namespace Godot
return (real_t)Math.Floor(s);
}
- public static real_t Fposmod(real_t x, real_t y)
- {
- if (x >= 0f)
- {
- return x % y;
- }
-
- return y - -x % y;
- }
-
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
- return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ return (weight - from) / (to - from);
}
public static bool IsInf(real_t s)
@@ -165,7 +155,7 @@ namespace Godot
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
- return from + (to - from) * Clamp(weight, 0f, 1f);
+ return from + (to - from) * weight;
}
public static real_t Log(real_t s)
@@ -210,6 +200,32 @@ namespace Godot
return new Vector2(r * Cos(th), r * Sin(th));
}
+ /// <summary>
+ /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// </summary>
+ public static real_t PosMod(real_t a, real_t b)
+ {
+ real_t c = a % b;
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ {
+ c += b;
+ }
+ return c;
+ }
+
+ /// <summary>
+ /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// </summary>
+ public static int PosMod(int a, int b)
+ {
+ int c = a % b;
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ {
+ c += b;
+ }
+ return c;
+ }
+
public static real_t Pow(real_t x, real_t y)
{
return (real_t)Math.Pow(x, y);
diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
index 00750edc07..7ed6ff82de 100755
--- a/modules/mono/glue/cs_files/VERSION.txt
+++ b/modules/mono/glue/cs_files/VERSION.txt
@@ -1 +1 @@
-3
+5
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index cedc8e9992..6a6f3062b4 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "builtin_types_glue.h"
+#include "collections_glue.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.h"
@@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) {
void godot_register_header_icalls() {
godot_register_builtin_type_icalls();
+ godot_register_collections_icalls();
}
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index 4e82bcd03e..12109045e0 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -34,15 +34,12 @@
uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
- return mono_gchandle_new(
- p_object,
- false /* do not pin the object */
- );
+ return mono_gchandle_new(p_object, /* pinned: */ false);
}
uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
- return mono_gchandle_new_weakref(p_object, false);
+ return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
}
Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0646580eaa..0f4e211be5 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -53,14 +53,6 @@
#include "main/main.h"
#endif
-void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
-
- (void)user_data; // UNUSED
-
- GDMonoUtils::print_unhandled_exception(exc);
- abort();
-}
-
#ifdef MONO_PRINT_HANDLER_ENABLED
void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
@@ -197,6 +189,8 @@ void GDMono::initialize() {
mono_config_parse(NULL);
+ mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime");
@@ -279,8 +273,6 @@ void GDMono::initialize() {
OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
#endif
- mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
-
OS::get_singleton()->print("Mono: INITIALIZED\n");
}
@@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() {
_GodotSharp::get_singleton()->_dispose_callback();
- MonoObject *ex = NULL;
- mono_domain_try_unload(domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINT("Exception thrown when unloading scripts domain:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINT("Exception thrown when unloading scripts domain");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
- MonoObject *ex = NULL;
- mono_domain_try_unload(p_domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(p_domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
assemblies.erase(p_domain_id);
}
+void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
+
+ // This method will be called by the runtime when a thrown exception is not handled.
+ // It won't be called when we manually treat a thrown exception as unhandled.
+ // We assume the exception was already printed before calling this hook.
+
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ abort();
+ _UNREACHABLE_();
+}
+
GDMono::GDMono() {
singleton = this;
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 5e01152870..e0ec6ced5e 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -164,6 +164,8 @@ public:
static GDMono *get_singleton() { return singleton; }
+ static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+
// Do not use these, unless you know what you're doing
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
GDMonoAssembly **get_loaded_assembly(const String &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 66339d7ae6..e2597a7d42 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -33,23 +33,37 @@
#include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.h"
+#include "gd_mono_marshal.h"
-MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
+String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
+ // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
+ MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
- return mono_class_get_type(p_class->get_mono_ptr());
-}
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+ return GDMonoMarshal::mono_string_to_godot(str);
+}
- return mono_class_is_assignable_from(mono_class, p_from->mono_class);
+MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
+ return mono_class_get_type(p_mono_class);
}
String GDMonoClass::get_full_name() const {
+ return get_full_name(mono_class);
+}
- String res = namespace_name;
- if (res.length())
- res += ".";
- return res + class_name;
+MonoType *GDMonoClass::get_mono_type() {
+ // Care, you cannot compare MonoType pointers
+ return get_mono_type(mono_class);
+}
+
+bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+
+ return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
GDMonoClass *GDMonoClass::get_parent_class() {
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 417c138594..f81ab84cd0 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -98,7 +98,11 @@ class GDMonoClass {
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public:
- static MonoType *get_raw_type(GDMonoClass *p_class);
+ static String get_full_name(MonoClass *p_mono_class);
+ static MonoType *get_mono_type(MonoClass *p_mono_class);
+
+ String get_full_name() const;
+ MonoType *get_mono_type();
bool is_assignable_from(GDMonoClass *p_from) const;
@@ -108,8 +112,6 @@ public:
_FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
- String get_full_name() const;
-
GDMonoClass *get_parent_class();
#ifdef TOOLS_ENABLED
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 3b91777ed4..d3a673dc1b 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
SET_FROM_ARRAY_AND_BREAK(Array);
@@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL();
} break;
@@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
case Variant::DICTIONARY: {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ } break;
+ case Variant::ARRAY: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
@@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 2b5110f0b9..72a5439044 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -45,7 +45,8 @@ struct ManagedType {
GDMonoClass *type_class;
ManagedType() {
- type_class = 0;
+ type_encoding = 0;
+ type_class = NULL;
}
};
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index a1a79f957f..505c030ca1 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -32,8 +32,12 @@
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
@@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
return;
}
+
+void unhandled_exception(MonoException *p_exc) {
+ mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+ abort();
+ _UNREACHABLE_();
+}
+
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index abec65e7d4..50cadcedf6 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -33,11 +33,20 @@
#include <mono/jit/jit.h>
+#include "../utils/macros.h"
+
#include "core/object.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
-}
+
+/**
+ * Do not call this function directly.
+ * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
+ */
+_NO_RETURN_ void unhandled_exception(MonoException *p_exc);
+
+} // namespace GDMonoInternals
#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c04fcca962..de91e71bab 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return Variant::ARRAY;
@@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(RID) == type_class) {
return Variant::_RID;
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return Variant::DICTIONARY;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return Variant::ARRAY;
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
return Variant::DICTIONARY;
}
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return Variant::ARRAY;
+ }
} break;
default: {
@@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
type.type_encoding = MONO_TYPE_OBJECT;
+ // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
return variant_to_mono_object(p_var, type);
}
@@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
@@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (CACHED_CLASS(RID) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator RID());
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+ }
} break;
case MONO_TYPE_OBJECT: {
// Variant
@@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
case Variant::DICTIONARY:
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
case Variant::ARRAY:
- return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
case Variant::POOL_BYTE_ARRAY:
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
case Variant::POOL_INT_ARRAY:
@@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
+ }
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
} break;
} break;
@@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!tclass, Variant());
- MonoType *raw_type = tclass->get_raw_type(tclass);
+ MonoType *raw_type = tclass->get_mono_type();
ManagedType type;
@@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj);
@@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
return ptr ? Variant(*ptr) : Variant();
}
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr);
+ Array *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr);
+ Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- return mono_object_to_Dictionary(p_obj);
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Dictionary *>(ret);
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Array *>(ret);
}
} break;
}
@@ -822,62 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
}
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
- MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
- MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
-
- int i = 0;
- const Variant *dkey = NULL;
- while ((dkey = p_dict.next(dkey))) {
- mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
- mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
- i++;
- }
-
- GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
-
- MonoObject *ex = NULL;
- MonoObject *ret = arrays_to_dict(keys, values, &ex);
-
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL_V(NULL);
- }
-
- return ret;
-}
-
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
- Dictionary ret;
-
- if (!p_dict)
- return ret;
-
- GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
-
- MonoArray *keys = NULL;
- MonoArray *values = NULL;
- MonoObject *ex = NULL;
- dict_to_arrays(p_dict, &keys, &values, &ex);
-
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL_V(Dictionary());
- }
-
- int length = mono_array_length(keys);
-
- for (int i = 0; i < length; i++) {
- MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
- MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
-
- Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
- Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
-
- ret[key] = value;
- }
-
- return ret;
-}
-}
+} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 6572408ab5..464f584a0a 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
-// Dictionary
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
-
#ifdef YOLO_COPY
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index ad52904945..c8df1038ce 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method);
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
@@ -104,28 +104,32 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
mono_array_set(params, MonoObject *, i, boxed_param);
}
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
} else {
- MonoObject *exc = NULL;
- mono_runtime_invoke(mono_method, p_object, NULL, &exc);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
@@ -133,21 +137,23 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
}
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
return invoke_raw(p_object, NULL, r_exc);
}
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc);
+MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index a173af83f4..444ec2a67d 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -71,9 +71,9 @@ public:
void *get_thunk();
- MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL);
- MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL);
- MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
+ MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 0fe527b199..a1c710c26c 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -138,47 +138,38 @@ bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != NULL;
}
-void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) {
- MonoMethod *prop_method = mono_property_get_set_method(mono_property);
-
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
- mono_array_set(params, MonoObject *, 0, p_value);
-
- MonoObject *exc = NULL;
- mono_runtime_invoke_array(prop_method, p_object, params, &exc);
-
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::print_unhandled_exception(exc);
- }
- }
+void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
+ void *params[1] = { p_value };
+ set_value(p_object, params, r_exc);
}
-void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- mono_property_set_value(mono_property, p_object, p_params, &exc);
+void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
}
-MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc);
+MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index 2a0065e850..b3f1e2114a 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -62,9 +62,9 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; }
- void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL);
- void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
- MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL);
+ void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL);
+ void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
+ MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index fbdbeaa3f7..7cd922138f 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -30,12 +30,15 @@
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
#include "os/dir_access.h"
#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
#include "../csharp_script.h"
+#include "../utils/macros.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
@@ -84,6 +87,8 @@ void MonoCache::clear_members() {
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
+ class_KeyNotFoundException = NULL;
+
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
@@ -104,6 +109,8 @@ void MonoCache::clear_members() {
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
+ class_Array = NULL;
+ class_Dictionary = NULL;
class_MarshalUtils = NULL;
#ifdef DEBUG_ENABLED
@@ -131,8 +138,10 @@ void MonoCache::clear_members() {
field_Image_ptr = NULL;
field_RID_ptr = NULL;
- methodthunk_MarshalUtils_DictionaryToArrays = NULL;
- methodthunk_MarshalUtils_ArraysToDictionary = NULL;
+ methodthunk_Array_GetPtr = NULL;
+ methodthunk_Dictionary_GetPtr = NULL;
+ methodthunk_MarshalUtils_IsArrayGenericType = NULL;
+ methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
@@ -172,6 +181,8 @@ void update_corlib_cache() {
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
+ CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
+
mono_cache.corlib_cache_updated = true;
}
@@ -195,6 +206,8 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+ CACHE_CLASS_AND_CHECK(Array, GODOT_API_CLASS(Array));
+ CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED
@@ -221,8 +234,10 @@ void update_godot_api_cache() {
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method("GetPtr", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method("GetPtr", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsArrayGenericType", 1)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsDictionaryGenericType", 1)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
@@ -231,24 +246,9 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
#endif
- {
- /*
- * TODO Right now we only support Dictionary<object, object>.
- * It would be great if we could support other key/value types
- * without forcing the user to copy the entries.
- */
- GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
- ERR_FAIL_NULL(method_get_dict_type);
- MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
- ERR_FAIL_NULL(dict_refl_type);
- MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
- ERR_FAIL_NULL(dict_type);
-
- CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
- }
-
+ // TODO Move to CSharpLanguage::init()
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- mono_runtime_object_init(task_scheduler);
+ GDMonoUtils::runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
@@ -301,6 +301,12 @@ MonoThread *get_current_thread() {
return mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_object_init(p_this_obj);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
}
@@ -355,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
return mono_object;
}
@@ -365,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
@@ -377,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
return mono_object;
}
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Array *new_array = memnew(Array(p_from));
+ void *args[1] = { &new_array };
+
+ MonoException *exc = NULL;
+ mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Dictionary *new_dict = memnew(Dictionary(p_from));
+ void *args[1] = { &new_dict };
+
+ MonoException *exc = NULL;
+ mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
MonoDomain *create_domain(const String &p_friendly_name) {
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
@@ -397,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
-String get_exception_name_and_message(MonoObject *p_ex) {
+String get_exception_name_and_message(MonoException *p_exc) {
String res;
- MonoClass *klass = mono_object_get_class(p_ex);
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
@@ -410,29 +476,43 @@ String get_exception_name_and_message(MonoObject *p_ex) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL);
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL);
+ GD_MONO_END_RUNTIME_INVOKE;
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
-void print_unhandled_exception(MonoObject *p_exc) {
- print_unhandled_exception(p_exc, false);
+void set_exception_message(MonoException *p_exc, String message) {
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
+ MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
+ MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
+ void *params[1] = { msg };
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
+void debug_print_unhandled_exception(MonoException *p_exc) {
+ print_unhandled_exception(p_exc);
+ debug_send_unhandled_exception_error(p_exc);
}
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
- mono_print_unhandled_exception(p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
if (!ScriptDebugger::get_singleton())
return;
+ _TLS_RECURSION_GUARD_;
+
ScriptLanguage::StackInfo separator;
- separator.file = "";
+ separator.file = String();
separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
separator.line = 0;
Vector<ScriptLanguage::StackInfo> si;
- String exc_msg = "";
+ String exc_msg;
while (p_exc != NULL) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
@@ -441,24 +521,16 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info };
- MonoObject *unexpected_exc = NULL;
+ MonoException *unexpected_exc = NULL;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
- if (unexpected_exc != NULL) {
- mono_print_unhandled_exception(unexpected_exc);
-
- if (p_recursion_caution) {
- // Called from CSharpLanguage::get_current_stack_info,
- // so printing an error here could result in endless recursion
- OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
- return;
- } else {
- ERR_FAIL();
- }
+ if (unexpected_exc) {
+ GDMonoInternals::unhandled_exception(unexpected_exc);
+ _UNREACHABLE_();
}
Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != NULL && !p_recursion_caution) {
+ if (stack_trace != NULL) {
_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
for (int i = _si.size() - 1; i >= 0; i--)
si.insert(0, _si[i]);
@@ -466,10 +538,15 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
- GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException");
- p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL;
- if (p_exc != NULL)
+ GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
+ GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
+ CRASH_COND(inner_exc_prop == NULL);
+
+ MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
+ if (inner_exc != NULL)
si.insert(0, separator);
+
+ p_exc = (MonoException *)inner_exc;
}
String file = si.size() ? si[0].file : __FILE__;
@@ -481,4 +558,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
#endif
}
+void debug_unhandled_exception(MonoException *p_exc) {
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
+ _UNREACHABLE_();
+}
+
+void print_unhandled_exception(MonoException *p_exc) {
+ mono_print_unhandled_exception((MonoObject *)p_exc);
+}
+
+void set_pending_exception(MonoException *p_exc) {
+#ifdef HAS_PENDING_EXCEPTIONS
+ if (get_runtime_invoke_count() == 0) {
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+ }
+
+ if (!mono_runtime_set_pending_exception(p_exc, false)) {
+ ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
+ GDMonoUtils::debug_print_unhandled_exception(p_exc);
+ }
+#else
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+#endif
+}
+
+_THREAD_LOCAL_(int)
+current_invoke_count = 0;
+
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index fc13a00e85..d6774ed41d 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -34,19 +34,31 @@
#include <mono/metadata/threads.h>
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_header.h"
#include "object.h"
#include "reference.h"
+#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
+ if (unlikely(m_exc != NULL)) { \
+ GDMonoUtils::debug_unhandled_exception(m_exc); \
+ _UNREACHABLE_(); \
+ }
+
namespace GDMonoUtils {
-typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
-typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
+typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
+typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache {
@@ -77,6 +89,8 @@ struct MonoCache {
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
+ GDMonoClass *class_KeyNotFoundException;
+
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@@ -98,6 +112,8 @@ struct MonoCache {
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
+ GDMonoClass *class_Array;
+ GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
#ifdef DEBUG_ENABLED
@@ -125,8 +141,10 @@ struct MonoCache {
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
- MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
- MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
+ Array_GetPtr methodthunk_Array_GetPtr;
+ Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
+ IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
+ IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
@@ -173,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() {
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj);
+
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
@@ -181,13 +201,34 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
MonoObject *create_managed_from(const NodePath &p_from);
MonoObject *create_managed_from(const RID &p_from);
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
MonoDomain *create_domain(const String &p_friendly_name);
-String get_exception_name_and_message(MonoObject *p_ex);
+String get_exception_name_and_message(MonoException *p_exc);
+void set_exception_message(MonoException *p_exc, String message);
-void print_unhandled_exception(MonoObject *p_exc);
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
+void debug_print_unhandled_exception(MonoException *p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc);
+_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc);
+void print_unhandled_exception(MonoException *p_exc);
+
+/**
+ * Sets the exception as pending. The exception will be thrown when returning to managed code.
+ * If no managed method is being invoked by the runtime, the exception will be treated as
+ * an unhandled exception and the method will not return.
+ */
+void set_pending_exception(MonoException *p_exc);
+
+extern _THREAD_LOCAL_(int) current_invoke_count;
+
+_FORCE_INLINE_ int get_runtime_invoke_count() {
+ return current_invoke_count;
+}
+_FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
+ return current_invoke_count;
+}
} // namespace GDMonoUtils
@@ -206,4 +247,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
+#define GD_MONO_BEGIN_RUNTIME_INVOKE \
+ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
+ _runtime_invoke_count_ref += 1;
+
+#define GD_MONO_END_RUNTIME_INVOKE \
+ _runtime_invoke_count_ref -= 1;
+
#endif // GD_MONOUTILS_H
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py
index 9c188d07a7..c8ebb54ded 100644
--- a/modules/mono/mono_reg_utils.py
+++ b/modules/mono/mono_reg_utils.py
@@ -60,10 +60,10 @@ def _find_mono_in_reg_old(subkey, bits):
def find_mono_root_dir(bits):
root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
if root_dir is not None:
- return root_dir
+ return str(root_dir)
root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
if root_dir is not None:
- return root_dir
+ return str(root_dir)
return ''
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index b9d8520ac9..54720652fa 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -101,11 +101,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback);
- MonoObject *ex = NULL;
- thunk(get_target(), signal_args, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(get_target(), signal_args, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Variant());
}
@@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
MonoObject *awaiter = get_target();
if (awaiter) {
- MonoObject *ex = NULL;
- thunk(awaiter, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(awaiter, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V();
}
}
diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py
new file mode 100644
index 0000000000..622280b00b
--- /dev/null
+++ b/modules/mono/tls_configure.py
@@ -0,0 +1,36 @@
+from __future__ import print_function
+
+def supported(result):
+ return 'supported' if result else 'not supported'
+
+
+def check_cxx11_thread_local(conf):
+ print('Checking for `thread_local` support...', end=" ")
+ result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_declspec_thread(conf):
+ print('Checking for `__declspec(thread)` support...', end=" ")
+ result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_gcc___thread(conf):
+ print('Checking for `__thread` support...', end=" ")
+ result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def configure(conf):
+ if check_cxx11_thread_local(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
+ else:
+ if conf.env.msvc:
+ if check_declspec_thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
+ elif check_gcc___thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
new file mode 100644
index 0000000000..337a86870e
--- /dev/null
+++ b/modules/mono/utils/macros.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* util_macros.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef UTIL_MACROS_H
+#define UTIL_MACROS_H
+
+// noreturn
+
+#undef _NO_RETURN_
+
+#ifdef __GNUC__
+#define _NO_RETURN_ __attribute__((noreturn))
+#elif _MSC_VER
+#define _NO_RETURN_ __declspec(noreturn)
+#else
+#error Platform or compiler not supported
+#endif
+
+// unreachable
+
+#if defined(_MSC_VER)
+#define _UNREACHABLE_() __assume(0)
+#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+#define _UNREACHABLE_() __builtin_unreachable()
+#else
+#define _UNREACHABLE_() \
+ CRASH_NOW(); \
+ do { \
+ } while (true);
+#endif
+
+#endif // UTIL_MACROS_H
diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp
new file mode 100644
index 0000000000..6f8b0f90bc
--- /dev/null
+++ b/modules/mono/utils/thread_local.cpp
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* thread_local.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "thread_local.h"
+
+#ifdef WINDOWS_ENABLED
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include "core/os/memory.h"
+#include "core/print_string.h"
+
+struct ThreadLocalStorage::Impl {
+
+#ifdef WINDOWS_ENABLED
+ DWORD dwFlsIndex;
+#else
+ pthread_key_t key;
+#endif
+
+ void *get_value() const {
+#ifdef WINDOWS_ENABLED
+ return FlsGetValue(dwFlsIndex);
+#else
+ return pthread_getspecific(key);
+#endif
+ }
+
+ void set_value(void *p_value) const {
+#ifdef WINDOWS_ENABLED
+ FlsSetValue(dwFlsIndex, p_value);
+#else
+ pthread_setspecific(key, p_value);
+#endif
+ }
+
+ Impl(void (*p_destr_callback_func)(void *)) {
+#ifdef WINDOWS_ENABLED
+ dwFlsIndex = FlsAlloc(p_destr_callback_func);
+ ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
+#else
+ pthread_key_create(&key, p_destr_callback_func);
+#endif
+ }
+
+ ~Impl() {
+#ifdef WINDOWS_ENABLED
+ FlsFree(dwFlsIndex);
+#else
+ pthread_key_delete(key);
+#endif
+ }
+};
+
+void *ThreadLocalStorage::get_value() const {
+ return pimpl->get_value();
+}
+
+void ThreadLocalStorage::set_value(void *p_value) const {
+ pimpl->set_value(p_value);
+}
+
+void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) {
+ pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
+}
+
+void ThreadLocalStorage::free() {
+ memdelete(pimpl);
+ pimpl = NULL;
+}
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
new file mode 100644
index 0000000000..7ff10b4efc
--- /dev/null
+++ b/modules/mono/utils/thread_local.h
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* thread_local.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef THREAD_LOCAL_H
+#define THREAD_LOCAL_H
+
+#ifdef HAVE_CXX11_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) thread_local m_t
+#else
+
+#if !defined(__GNUC__) && !defined(_MSC_VER)
+#error Platform or compiler not supported
+#endif
+
+#ifdef __GNUC__
+
+#ifdef HAVE_GCC___THREAD
+#define _THREAD_LOCAL_(m_t) __thread m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#elif _MSC_VER
+
+#ifdef HAVE_DECLSPEC_THREAD
+#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#endif // __GNUC__ _MSC_VER
+
+#endif // HAVE_CXX11_THREAD_LOCAL
+
+#ifdef USE_CUSTOM_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
+#endif
+
+#include "core/typedefs.h"
+
+struct ThreadLocalStorage {
+
+ void *get_value() const;
+ void set_value(void *p_value) const;
+
+ void alloc(void (*p_dest_callback)(void *));
+ void free();
+
+private:
+ struct Impl;
+ Impl *pimpl;
+};
+
+template <typename T>
+class ThreadLocal {
+
+ ThreadLocalStorage storage;
+
+ T init_val;
+
+#ifdef WINDOWS_ENABLED
+#define _CALLBACK_FUNC_ __stdcall
+#else
+#define _CALLBACK_FUNC_
+#endif
+
+ static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
+ memdelete(static_cast<T *>(tls_data));
+ }
+
+#undef _CALLBACK_FUNC_
+
+ T *_tls_get_value() const {
+ void *tls_data = storage.get_value();
+
+ if (tls_data)
+ return static_cast<T *>(tls_data);
+
+ T *data = memnew(T(init_val));
+
+ storage.set_value(data);
+
+ return data;
+ }
+
+public:
+ ThreadLocal() :
+ ThreadLocal(T()) {}
+
+ ThreadLocal(const T &p_init_val) :
+ init_val(p_init_val) {
+ storage.alloc(&destr_callback);
+ }
+
+ ThreadLocal(const ThreadLocal &other) :
+ ThreadLocal(*other._tls_get_value()) {}
+
+ ~ThreadLocal() {
+ storage.free();
+ }
+
+ _FORCE_INLINE_ T *operator&() const {
+ return _tls_get_value();
+ }
+
+ _FORCE_INLINE_ operator T &() const {
+ return *_tls_get_value();
+ }
+
+ _FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
+ T *ptr = _tls_get_value();
+ *ptr = val;
+ return *this;
+ }
+};
+
+struct FlagScopeGuard {
+
+ FlagScopeGuard(bool &p_flag) :
+ flag(p_flag) {
+ flag = !flag;
+ }
+
+ ~FlagScopeGuard() {
+ flag = !flag;
+ }
+
+private:
+ bool &flag;
+};
+
+#define _TLS_RECURSION_GUARD_V_(m_ret) \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return m_ret; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#define _TLS_RECURSION_GUARD_ \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#endif // THREAD_LOCAL_H
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 8174bccdb7..f5d35714e1 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -240,11 +240,11 @@ ResourceFormatPVR::ResourceFormatPVR() {
Image::_image_compress_pvrtc2_func = _compress_pvrtc4;
}
- /////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////
- //PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
+//PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
- /////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////
#define PT_INDEX 2
#define BLK_Y_SIZE 4
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index 18b4051afe..4b8d5e9283 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -19,10 +19,13 @@ if env['builtin_pcre2']:
"pcre2_compile.c",
"pcre2_config.c",
"pcre2_context.c",
+ "pcre2_convert.c",
"pcre2_dfa_match.c",
"pcre2_error.c",
+ "pcre2_extuni.c",
"pcre2_find_bracket.c",
"pcre2_jit_compile.c",
+ #"pcre2_jit_match.c", "pcre2_jit_misc.c", # these files are included in pcre2_jit_compile.c.
"pcre2_maketables.c",
"pcre2_match.c",
"pcre2_match_data.c",
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 7391bed699..d4fa88afa7 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -250,8 +250,9 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
if (tga_header.image_width <= 0 || tga_header.image_height <= 0)
err = FAILED;
- if (tga_header.pixel_depth != 8 && tga_header.pixel_depth != 24 && tga_header.pixel_depth != 32)
+ if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
err = FAILED;
+ }
if (err == OK) {
f->seek(f->get_position() + tga_header.id_length);
diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml
deleted file mode 100644
index 5fc40a6eba..0000000000
--- a/modules/theora/doc_classes/ResourceImporterTheora.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index 9bc5ed903a..971fe39c44 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_theora.h"
+
#include "video_stream_theora.h"
+static ResourceFormatLoaderTheora *resource_loader_theora = NULL;
+
void register_theora_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterTheora> theora_import;
- theora_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(theora_import);
-#endif
+ resource_loader_theora = memnew(ResourceFormatLoaderTheora);
+ ResourceLoader::add_resource_format_loader(resource_loader_theora, true);
+
ClassDB::register_class<VideoStreamTheora>();
}
void unregister_theora_types() {
+
+ if (resource_loader_theora) {
+ memdelete(resource_loader_theora);
+ }
}
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 9e6307c0bf..881808873b 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -730,3 +730,46 @@ void VideoStreamTheora::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
}
+
+////////////
+
+RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ if (r_error) {
+ *r_error = ERR_CANT_OPEN;
+ }
+ memdelete(f);
+ return RES();
+ }
+
+ VideoStreamTheora *stream = memnew(VideoStreamTheora);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return ogv_stream;
+}
+
+void ResourceFormatLoaderTheora::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ogv");
+}
+
+bool ResourceFormatLoaderTheora::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderTheora::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "ogv")
+ return "VideoStreamTheora";
+ return "";
+}
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 4bdbbdae20..7cee1b491b 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -163,7 +163,7 @@ public:
class VideoStreamTheora : public VideoStream {
GDCLASS(VideoStreamTheora, VideoStream);
- RES_BASE_EXTENSION("ogvstr");
+ RES_BASE_EXTENSION("ogv");
String file;
int audio_track;
@@ -186,4 +186,12 @@ public:
VideoStreamTheora() { audio_track = 0; }
};
+class ResourceFormatLoaderTheora : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
#endif
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 9331092171..af9fea6681 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2402,7 +2402,7 @@ void VisualScriptLanguage::make_template(const String &p_class_name, const Strin
script->set_instance_base_type(p_base_class_name);
}
-bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const {
return false;
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index aaa6dfea11..b163203a3a 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -563,7 +563,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 0618064ea6..873cc293c9 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -3389,6 +3389,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
+ graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
graph->connect("node_selected", this, "_node_selected");
graph->connect("_begin_node_move", this, "_begin_node_move");
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 72b5e09222..0bd64d6a1d 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -137,7 +137,7 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String> > args;
};
- HashMap<StringName, Ref<StyleBox>, StringNameHasher> node_styles;
+ HashMap<StringName, Ref<StyleBox> > node_styles;
StringName edited_func;
void _update_graph_connections();
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index bae8f7be5f..5bc82f267f 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -48,7 +48,7 @@ size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst, size_t p_data, s
int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f, ogg_int64_t offs, int whence) {
-//printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
+ //printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
#ifdef SEEK_SET
//printf("seek set defined\n");
diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml
deleted file mode 100644
index 0cfab1baf0..0000000000
--- a/modules/webm/doc_classes/ResourceImporterWebm.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp
index 1183dd41f7..121b528d5b 100644
--- a/modules/webm/register_types.cpp
+++ b/modules/webm/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_webm.h"
+
#include "video_stream_webm.h"
+static ResourceFormatLoaderWebm *resource_loader_webm = NULL;
+
void register_webm_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterWebm> webm_import;
- webm_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(webm_import);
-#endif
+ resource_loader_webm = memnew(ResourceFormatLoaderWebm);
+ ResourceLoader::add_resource_format_loader(resource_loader_webm, true);
+
ClassDB::register_class<VideoStreamWebm>();
}
void unregister_webm_types() {
+
+ if (resource_loader_webm) {
+ memdelete(resource_loader_webm);
+ }
}
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index fac47225bc..1bb9a43886 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -443,3 +443,46 @@ void VideoStreamWebm::set_audio_track(int p_track) {
audio_track = p_track;
}
+
+////////////
+
+RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ if (r_error) {
+ *r_error = ERR_CANT_OPEN;
+ }
+ memdelete(f);
+ return RES();
+ }
+
+ VideoStreamWebm *stream = memnew(VideoStreamWebm);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return webm_stream;
+}
+
+void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("webm");
+}
+
+bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "webm")
+ return "VideoStreamWebm";
+ return "";
+}
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
index dde993d154..08be50846d 100644
--- a/modules/webm/video_stream_webm.h
+++ b/modules/webm/video_stream_webm.h
@@ -109,7 +109,7 @@ private:
class VideoStreamWebm : public VideoStream {
GDCLASS(VideoStreamWebm, VideoStream);
- RES_BASE_EXTENSION("webmstr");
+ RES_BASE_EXTENSION("webm");
String file;
int audio_track;
@@ -127,4 +127,12 @@ public:
virtual void set_audio_track(int p_track);
};
+class ResourceFormatLoaderWebm : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
#endif // VIDEO_STREAM_WEBM_H
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index cdf2d75e96..42b2c77777 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -116,64 +116,73 @@ static Ref<Image> _webp_lossy_unpack(const PoolVector<uint8_t> &p_buffer) {
return img;
}
-Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
- uint32_t size = f->get_len();
- PoolVector<uint8_t> src_image;
- src_image.resize(size);
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
WebPBitstreamFeatures features;
-
- PoolVector<uint8_t>::Write src_w = src_image.write();
- f->get_buffer(src_w.ptr(), size);
- ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_EOF);
-
- if (WebPGetFeatures(src_w.ptr(), size, &features) != VP8_STATUS_OK) {
- f->close();
- //ERR_EXPLAIN("Error decoding WEBP image: "+p_file);
+ if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
+ // ERR_EXPLAIN("Error decoding WEBP image");
ERR_FAIL_V(ERR_FILE_CORRUPT);
}
- /*
- print_line("width: " + itos(features.width));
- print_line("height: " + itos(features.height));
- print_line("alpha: " + itos(features.has_alpha));
- */
-
- src_w = PoolVector<uint8_t>::Write();
-
PoolVector<uint8_t> dst_image;
int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
dst_image.resize(datasize);
-
- PoolVector<uint8_t>::Read src_r = src_image.read();
PoolVector<uint8_t>::Write dst_w = dst_image.write();
bool errdec = false;
if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(src_r.ptr(), size, dst_w.ptr(), datasize, 4 * features.width) == NULL;
+ errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w.ptr(), datasize, 4 * features.width) == NULL;
} else {
- errdec = WebPDecodeRGBInto(src_r.ptr(), size, dst_w.ptr(), datasize, 3 * features.width) == NULL;
+ errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w.ptr(), datasize, 3 * features.width) == NULL;
}
+ dst_w = PoolVector<uint8_t>::Write();
- //ERR_EXPLAIN("Error decoding webp! - "+p_file);
+ //ERR_EXPLAIN("Error decoding webp!");
ERR_FAIL_COND_V(errdec, ERR_FILE_CORRUPT);
- src_r = PoolVector<uint8_t>::Read();
- dst_w = PoolVector<uint8_t>::Write();
-
p_image->create(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
return OK;
}
+static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
+
+ Ref<Image> img;
+ img.instance();
+ Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
+ ERR_FAIL_COND_V(err, Ref<Image>());
+ return img;
+}
+
+Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
+
+ PoolVector<uint8_t> src_image;
+ int src_image_len = f->get_len();
+ ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
+ src_image.resize(src_image_len);
+
+ PoolVector<uint8_t>::Write w = src_image.write();
+
+ f->get_buffer(&w[0], src_image_len);
+
+ f->close();
+
+ Error err = webp_load_image_from_buffer(p_image.ptr(), w.ptr(), src_image_len);
+
+ w = PoolVector<uint8_t>::Write();
+
+ return err;
+}
+
void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("webp");
}
ImageLoaderWEBP::ImageLoaderWEBP() {
-
+ Image::_webp_mem_loader_func = _webp_mem_loader_func;
Image::lossy_packer = _webp_lossy_pack;
Image::lossy_unpacker = _webp_lossy_unpack;
}
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
index a850a545d3..85a1e3769f 100644
--- a/modules/websocket/lws_helper.h
+++ b/modules/websocket/lws_helper.h
@@ -30,6 +30,9 @@
#ifndef LWS_HELPER_H
#define LWS_HELPER_H
+#define LWS_BUF_SIZE 65536
+#define LWS_PACKET_SIZE LWS_BUF_SIZE
+
#include "core/io/stream_peer.h"
#include "core/os/os.h"
#include "core/reference.h"
@@ -124,6 +127,7 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback,
/* LWS protocol structs */
ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2));
+ memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2));
CharString strings = p_names.join(",").ascii();
int str_len = strings.length();
@@ -145,13 +149,15 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback,
structs_ptr[0].name = "http-only";
structs_ptr[0].callback = p_callback;
structs_ptr[0].per_session_data_size = data_size;
- structs_ptr[0].rx_buffer_size = 0;
+ structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE;
/* add user defined protocols */
for (i = 0; i < len; i++) {
structs_ptr[i + 1].name = (const char *)&names_ptr[pos];
structs_ptr[i + 1].callback = p_callback;
structs_ptr[i + 1].per_session_data_size = data_size;
- structs_ptr[i + 1].rx_buffer_size = 0;
+ structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE;
pos += pnr[i].ascii().length() + 1;
names_ptr[pos - 1] = '\0';
}
@@ -209,6 +215,6 @@ public: \
\
protected:
- /* clang-format on */
+/* clang-format on */
#endif // LWS_HELPER_H
diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template
index 13d10b5026..81f4c15849 100644
--- a/platform/android/AndroidManifest.xml.template
+++ b/platform/android/AndroidManifest.xml.template
@@ -32,174 +32,9 @@
$$ADD_APPLICATION_CHUNKS$$
</application>
- <uses-feature android:glEsVersion="0x00030000" android:required="true" />
+ <uses-feature android:glEsVersion="0x00020000" android:required="true" />
$$ADD_PERMISSION_CHUNKS$$
-<uses-permission android:name="godot.ACCESS_CHECKIN_PROPERTIES"/>
-<uses-permission android:name="godot.ACCESS_COARSE_LOCATION"/>
-<uses-permission android:name="godot.ACCESS_FINE_LOCATION"/>
-<uses-permission android:name="godot.ACCESS_LOCATION_EXTRA_COMMANDS"/>
-<uses-permission android:name="godot.ACCESS_MOCK_LOCATION"/>
-<uses-permission android:name="godot.ACCESS_NETWORK_STATE"/>
-<uses-permission android:name="godot.ACCESS_SURFACE_FLINGER"/>
-<uses-permission android:name="godot.ACCESS_WIFI_STATE"/>
-<uses-permission android:name="godot.ACCOUNT_MANAGER"/>
-<uses-permission android:name="godot.ADD_VOICEMAIL"/>
-<uses-permission android:name="godot.AUTHENTICATE_ACCOUNTS"/>
-<uses-permission android:name="godot.BATTERY_STATS"/>
-<uses-permission android:name="godot.BIND_ACCESSIBILITY_SERVICE"/>
-<uses-permission android:name="godot.BIND_APPWIDGET"/>
-<uses-permission android:name="godot.BIND_DEVICE_ADMIN"/>
-<uses-permission android:name="godot.BIND_INPUT_METHOD"/>
-<uses-permission android:name="godot.BIND_NFC_SERVICE"/>
-<uses-permission android:name="godot.BIND_NOTIFICATION_LISTENER_SERVICE"/>
-<uses-permission android:name="godot.BIND_PRINT_SERVICE"/>
-<uses-permission android:name="godot.BIND_REMOTEVIEWS"/>
-<uses-permission android:name="godot.BIND_TEXT_SERVICE"/>
-<uses-permission android:name="godot.BIND_VPN_SERVICE"/>
-<uses-permission android:name="godot.BIND_WALLPAPER"/>
-<uses-permission android:name="godot.BLUETOOTH"/>
-<uses-permission android:name="godot.BLUETOOTH_ADMIN"/>
-<uses-permission android:name="godot.BLUETOOTH_PRIVILEGED"/>
-<uses-permission android:name="godot.BRICK"/>
-<uses-permission android:name="godot.BROADCAST_PACKAGE_REMOVED"/>
-<uses-permission android:name="godot.BROADCAST_SMS"/>
-<uses-permission android:name="godot.BROADCAST_STICKY"/>
-<uses-permission android:name="godot.BROADCAST_WAP_PUSH"/>
-<uses-permission android:name="godot.CALL_PHONE"/>
-<uses-permission android:name="godot.CALL_PRIVILEGED"/>
-<uses-permission android:name="godot.CAMERA"/>
-<uses-permission android:name="godot.CAPTURE_AUDIO_OUTPUT"/>
-<uses-permission android:name="godot.CAPTURE_SECURE_VIDEO_OUTPUT"/>
-<uses-permission android:name="godot.CAPTURE_VIDEO_OUTPUT"/>
-<uses-permission android:name="godot.CHANGE_COMPONENT_ENABLED_STATE"/>
-<uses-permission android:name="godot.CHANGE_CONFIGURATION"/>
-<uses-permission android:name="godot.CHANGE_NETWORK_STATE"/>
-<uses-permission android:name="godot.CHANGE_WIFI_MULTICAST_STATE"/>
-<uses-permission android:name="godot.CHANGE_WIFI_STATE"/>
-<uses-permission android:name="godot.CLEAR_APP_CACHE"/>
-<uses-permission android:name="godot.CLEAR_APP_USER_DATA"/>
-<uses-permission android:name="godot.CONTROL_LOCATION_UPDATES"/>
-<uses-permission android:name="godot.DELETE_CACHE_FILES"/>
-<uses-permission android:name="godot.DELETE_PACKAGES"/>
-<uses-permission android:name="godot.DEVICE_POWER"/>
-<uses-permission android:name="godot.DIAGNOSTIC"/>
-<uses-permission android:name="godot.DISABLE_KEYGUARD"/>
-<uses-permission android:name="godot.DUMP"/>
-<uses-permission android:name="godot.EXPAND_STATUS_BAR"/>
-<uses-permission android:name="godot.FACTORY_TEST"/>
-<uses-permission android:name="godot.FLASHLIGHT"/>
-<uses-permission android:name="godot.FORCE_BACK"/>
-<uses-permission android:name="godot.GET_ACCOUNTS"/>
-<uses-permission android:name="godot.GET_PACKAGE_SIZE"/>
-<uses-permission android:name="godot.GET_TASKS"/>
-<uses-permission android:name="godot.GET_TOP_ACTIVITY_INFO"/>
-<uses-permission android:name="godot.GLOBAL_SEARCH"/>
-<uses-permission android:name="godot.HARDWARE_TEST"/>
-<uses-permission android:name="godot.INJECT_EVENTS"/>
-<uses-permission android:name="godot.INSTALL_LOCATION_PROVIDER"/>
-<uses-permission android:name="godot.INSTALL_PACKAGES"/>
-<uses-permission android:name="godot.INSTALL_SHORTCUT"/>
-<uses-permission android:name="godot.INTERNAL_SYSTEM_WINDOW"/>
-<uses-permission android:name="godot.INTERNET"/>
-<uses-permission android:name="godot.KILL_BACKGROUND_PROCESSES"/>
-<uses-permission android:name="godot.LOCATION_HARDWARE"/>
-<uses-permission android:name="godot.MANAGE_ACCOUNTS"/>
-<uses-permission android:name="godot.MANAGE_APP_TOKENS"/>
-<uses-permission android:name="godot.MANAGE_DOCUMENTS"/>
-<uses-permission android:name="godot.MASTER_CLEAR"/>
-<uses-permission android:name="godot.MEDIA_CONTENT_CONTROL"/>
-<uses-permission android:name="godot.MODIFY_AUDIO_SETTINGS"/>
-<uses-permission android:name="godot.MODIFY_PHONE_STATE"/>
-<uses-permission android:name="godot.MOUNT_FORMAT_FILESYSTEMS"/>
-<uses-permission android:name="godot.MOUNT_UNMOUNT_FILESYSTEMS"/>
-<uses-permission android:name="godot.NFC"/>
-<uses-permission android:name="godot.PERSISTENT_ACTIVITY"/>
-<uses-permission android:name="godot.PROCESS_OUTGOING_CALLS"/>
-<uses-permission android:name="godot.READ_CALENDAR"/>
-<uses-permission android:name="godot.READ_CALL_LOG"/>
-<uses-permission android:name="godot.READ_CONTACTS"/>
-<uses-permission android:name="godot.READ_EXTERNAL_STORAGE"/>
-<uses-permission android:name="godot.READ_FRAME_BUFFER"/>
-<uses-permission android:name="godot.READ_HISTORY_BOOKMARKS"/>
-<uses-permission android:name="godot.READ_INPUT_STATE"/>
-<uses-permission android:name="godot.READ_LOGS"/>
-<uses-permission android:name="godot.READ_PHONE_STATE"/>
-<uses-permission android:name="godot.READ_PROFILE"/>
-<uses-permission android:name="godot.READ_SMS"/>
-<uses-permission android:name="godot.READ_SOCIAL_STREAM"/>
-<uses-permission android:name="godot.READ_SYNC_SETTINGS"/>
-<uses-permission android:name="godot.READ_SYNC_STATS"/>
-<uses-permission android:name="godot.READ_USER_DICTIONARY"/>
-<uses-permission android:name="godot.REBOOT"/>
-<uses-permission android:name="godot.RECEIVE_BOOT_COMPLETED"/>
-<uses-permission android:name="godot.RECEIVE_MMS"/>
-<uses-permission android:name="godot.RECEIVE_SMS"/>
-<uses-permission android:name="godot.RECEIVE_WAP_PUSH"/>
-<uses-permission android:name="godot.RECORD_AUDIO"/>
-<uses-permission android:name="godot.REORDER_TASKS"/>
-<uses-permission android:name="godot.RESTART_PACKAGES"/>
-<uses-permission android:name="godot.SEND_RESPOND_VIA_MESSAGE"/>
-<uses-permission android:name="godot.SEND_SMS"/>
-<uses-permission android:name="godot.SET_ACTIVITY_WATCHER"/>
-<uses-permission android:name="godot.SET_ALARM"/>
-<uses-permission android:name="godot.SET_ALWAYS_FINISH"/>
-<uses-permission android:name="godot.SET_ANIMATION_SCALE"/>
-<uses-permission android:name="godot.SET_DEBUG_APP"/>
-<uses-permission android:name="godot.SET_ORIENTATION"/>
-<uses-permission android:name="godot.SET_POINTER_SPEED"/>
-<uses-permission android:name="godot.SET_PREFERRED_APPLICATIONS"/>
-<uses-permission android:name="godot.SET_PROCESS_LIMIT"/>
-<uses-permission android:name="godot.SET_TIME"/>
-<uses-permission android:name="godot.SET_TIME_ZONE"/>
-<uses-permission android:name="godot.SET_WALLPAPER"/>
-<uses-permission android:name="godot.SET_WALLPAPER_HINTS"/>
-<uses-permission android:name="godot.SIGNAL_PERSISTENT_PROCESSES"/>
-<uses-permission android:name="godot.STATUS_BAR"/>
-<uses-permission android:name="godot.SUBSCRIBED_FEEDS_READ"/>
-<uses-permission android:name="godot.SUBSCRIBED_FEEDS_WRITE"/>
-<uses-permission android:name="godot.SYSTEM_ALERT_WINDOW"/>
-<uses-permission android:name="godot.TRANSMIT_IR"/>
-<uses-permission android:name="godot.UNINSTALL_SHORTCUT"/>
-<uses-permission android:name="godot.UPDATE_DEVICE_STATS"/>
-<uses-permission android:name="godot.USE_CREDENTIALS"/>
-<uses-permission android:name="godot.USE_SIP"/>
-<uses-permission android:name="godot.VIBRATE"/>
-<uses-permission android:name="godot.WAKE_LOCK"/>
-<uses-permission android:name="godot.WRITE_APN_SETTINGS"/>
-<uses-permission android:name="godot.WRITE_CALENDAR"/>
-<uses-permission android:name="godot.WRITE_CALL_LOG"/>
-<uses-permission android:name="godot.WRITE_CONTACTS"/>
-<uses-permission android:name="godot.WRITE_EXTERNAL_STORAGE"/>
-<uses-permission android:name="godot.WRITE_GSERVICES"/>
-<uses-permission android:name="godot.WRITE_HISTORY_BOOKMARKS"/>
-<uses-permission android:name="godot.WRITE_PROFILE"/>
-<uses-permission android:name="godot.WRITE_SECURE_SETTINGS"/>
-<uses-permission android:name="godot.WRITE_SETTINGS"/>
-<uses-permission android:name="godot.WRITE_SMS"/>
-<uses-permission android:name="godot.WRITE_SOCIAL_STREAM"/>
-<uses-permission android:name="godot.WRITE_SYNC_SETTINGS"/>
-<uses-permission android:name="godot.WRITE_USER_DICTIONARY"/>
-<uses-permission android:name="godot.custom.0"/>
-<uses-permission android:name="godot.custom.1"/>
-<uses-permission android:name="godot.custom.2"/>
-<uses-permission android:name="godot.custom.3"/>
-<uses-permission android:name="godot.custom.4"/>
-<uses-permission android:name="godot.custom.5"/>
-<uses-permission android:name="godot.custom.6"/>
-<uses-permission android:name="godot.custom.7"/>
-<uses-permission android:name="godot.custom.8"/>
-<uses-permission android:name="godot.custom.9"/>
-<uses-permission android:name="godot.custom.10"/>
-<uses-permission android:name="godot.custom.11"/>
-<uses-permission android:name="godot.custom.12"/>
-<uses-permission android:name="godot.custom.13"/>
-<uses-permission android:name="godot.custom.14"/>
-<uses-permission android:name="godot.custom.15"/>
-<uses-permission android:name="godot.custom.16"/>
-<uses-permission android:name="godot.custom.17"/>
-<uses-permission android:name="godot.custom.18"/>
-<uses-permission android:name="godot.custom.19"/>
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="27"/>
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 8c08289932..a65dab9668 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -53,7 +53,7 @@ if len(env.android_flat_dirs) > 0:
gradle_maven_flat_text = gradle_maven_flat_text[:-1]
gradle_maven_flat_text += "\n\t}\n"
-
+
gradle_maven_repos_text = ""
gradle_maven_repos_text += gradle_maven_flat_text
@@ -99,6 +99,9 @@ for x in env.android_jni_dirs:
gradle_asset_dirs_text = ""
+for x in env.android_asset_dirs:
+ gradle_asset_dirs_text += ",'" + x.replace("\\", "/") + "'"
+
gradle_default_config_text = ""
minSdk = 18
diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp
index 3d80e76707..b9f1f1eab0 100644
--- a/platform/android/audio_driver_jandroid.cpp
+++ b/platform/android/audio_driver_jandroid.cpp
@@ -78,9 +78,9 @@ Error AudioDriverAndroid::init() {
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
JNIEnv *env = ThreadAndroid::get_env();
- int mix_rate = GLOBAL_DEF("audio/mix_rate", 44100);
+ int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", 44100);
- int latency = GLOBAL_DEF("audio/output_latency", 25);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", 25);
unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000);
if (OS::get_singleton()->is_stdout_verbose()) {
print_line("audio buffer size: " + itos(buffer_size));
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index e6bd3ff253..fd834193dd 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -42,7 +42,8 @@ void AudioDriverOpenSL::_buffer_callback(
/* SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
- SLuint32 dataUsed*/) {
+ SLuint32 dataUsed*/
+) {
bool mix = true;
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index 2022bad02a..88cb122414 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -74,7 +74,8 @@ class AudioDriverOpenSL : public AudioDriver {
/* SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
- SLuint32 dataUsed*/);
+ SLuint32 dataUsed*/
+ );
static void _buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 971368db17..ada36e2814 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -139,8 +139,13 @@ def configure(env):
## Build type
if (env["target"].startswith("release")):
- env.Append(LINKFLAGS=['-O2'])
- env.Append(CPPFLAGS=['-O2', '-DNDEBUG', '-ffast-math', '-funsafe-math-optimizations', '-fomit-frame-pointer'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Append(LINKFLAGS=['-O2'])
+ env.Append(CPPFLAGS=['-O2', '-DNDEBUG', '-ffast-math', '-funsafe-math-optimizations', '-fomit-frame-pointer'])
+ else: #optimize for size
+ env.Append(CPPFLAGS=['-Os', '-DNDEBUG'])
+ env.Append(LINKFLAGS=['-Os'])
+
if (can_vectorize):
env.Append(CPPFLAGS=['-ftree-vectorize'])
if (env["target"] == "release_debug"):
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 9e6377f4fe..c562a47b00 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -228,7 +228,7 @@ class EditorExportAndroid : public EditorExportPlatform {
};
Vector<Device> devices;
- bool devices_changed;
+ volatile bool devices_changed;
Mutex *device_lock;
Thread *device_thread;
volatile bool quit_request;
@@ -570,7 +570,7 @@ class EditorExportAndroid : public EditorExportPlatform {
// const int CHUNK_RESOURCEIDS = 0x00080180;
const int CHUNK_STRINGS = 0x001C0001;
// const int CHUNK_XML_END_NAMESPACE = 0x00100101;
- // const int CHUNK_XML_END_TAG = 0x00100103;
+ const int CHUNK_XML_END_TAG = 0x00100103;
// const int CHUNK_XML_START_NAMESPACE = 0x00100100;
const int CHUNK_XML_START_TAG = 0x00100102;
// const int CHUNK_XML_TEXT = 0x00100104;
@@ -601,24 +601,28 @@ class EditorExportAndroid : public EditorExportPlatform {
bool screen_support_large = p_preset->get("screen/support_large");
bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
- String user_perms[MAX_USER_PERMISSIONS];
-
- for (int i = 0; i < MAX_USER_PERMISSIONS; i++) {
-
- user_perms[i] = p_preset->get("user_permissions/" + itos(i));
- }
-
- Set<String> perms;
+ Vector<String> perms;
const char **aperms = android_perms;
while (*aperms) {
bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
if (enabled)
- perms.insert(String(*aperms));
+ perms.push_back("android.permission." + String(*aperms));
aperms++;
}
+ for (int i = 0; i < MAX_USER_PERMISSIONS; i++) {
+ String user_perm = p_preset->get("user_permissions/" + itos(i));
+ if (user_perm.strip_edges() != "" && user_perm.strip_edges() != "False")
+ perms.push_back(user_perm.strip_edges());
+ }
+
+ if (p_give_internet) {
+ if (perms.find("android.permission.INTERNET") == -1)
+ perms.push_back("android.permission.INTERNET");
+ }
+
while (ofs < (uint32_t)p_manifest.size()) {
uint32_t chunk = decode_uint32(&p_manifest[ofs]);
@@ -741,27 +745,6 @@ class EditorExportAndroid : public EditorExportPlatform {
print_line("version number: " + itos(decode_uint32(&p_manifest[iofs + 16])));
}
- if (tname == "uses-permission" && /*nspace=="android" &&*/ attrname == "name") {
-
- if (value.begins_with("godot.custom")) {
-
- int which = value.get_slice(".", 2).to_int();
- if (which >= 0 && which < MAX_USER_PERMISSIONS && user_perms[which].strip_edges() != "") {
-
- string_table[attr_value] = user_perms[which].strip_edges();
- }
-
- } else if (value.begins_with("godot.")) {
- String perm = value.get_slice(".", 1);
-
- if (perms.has(perm) || (p_give_internet && perm == "INTERNET")) {
-
- print_line("PERM: " + perm);
- string_table[attr_value] = "android.permission." + perm;
- }
- }
- }
-
if (tname == "supports-screens") {
if (attrname == "smallScreens") {
@@ -786,6 +769,91 @@ class EditorExportAndroid : public EditorExportPlatform {
}
} break;
+ case CHUNK_XML_END_TAG: {
+ int iofs = ofs + 8;
+ uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
+ String tname = string_table[name];
+
+ if (tname == "manifest") {
+ print_line("Found manifest end");
+
+ // save manifest ending so we can restore it
+ Vector<uint8_t> manifest_end;
+ uint32_t manifest_cur_size = p_manifest.size();
+ uint32_t node_size = size;
+
+ manifest_end.resize(p_manifest.size() - ofs);
+ memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
+
+ int32_t attr_name_string = string_table.find("name");
+ ERR_EXPLAIN("Template does not have 'name' attribute");
+ ERR_FAIL_COND(attr_name_string == -1);
+
+ int32_t ns_android_string = string_table.find("android");
+ ERR_EXPLAIN("Template does not have 'android' namespace");
+ ERR_FAIL_COND(ns_android_string == -1);
+
+ int32_t attr_uses_permission_string = string_table.find("uses-permission");
+ if (attr_uses_permission_string == -1) {
+ string_table.push_back("uses-permission");
+ attr_uses_permission_string = string_table.size() - 1;
+ }
+
+ for (int i = 0; i < perms.size(); ++i) {
+ print_line("Adding permission " + perms[i]);
+
+ manifest_cur_size += 56 + 24; // node + end node
+ p_manifest.resize(manifest_cur_size);
+
+ // Add permission to the string pool
+ int32_t perm_string = string_table.find(perms[i]);
+ if (perm_string == -1) {
+ string_table.push_back(perms[i]);
+ perm_string = string_table.size() - 1;
+ }
+
+ // start tag
+ encode_uint16(0x102, &p_manifest[ofs]); // type
+ encode_uint16(16, &p_manifest[ofs + 2]); // headersize
+ encode_uint32(56, &p_manifest[ofs + 4]); // size
+ encode_uint32(0, &p_manifest[ofs + 8]); // lineno
+ encode_uint32(-1, &p_manifest[ofs + 12]); // comment
+ encode_uint32(-1, &p_manifest[ofs + 16]); // ns
+ encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name
+ encode_uint16(20, &p_manifest[ofs + 24]); // attr_start
+ encode_uint16(20, &p_manifest[ofs + 26]); // attr_size
+ encode_uint16(1, &p_manifest[ofs + 28]); // num_attrs
+ encode_uint16(0, &p_manifest[ofs + 30]); // id_index
+ encode_uint16(0, &p_manifest[ofs + 32]); // class_index
+ encode_uint16(0, &p_manifest[ofs + 34]); // style_index
+
+ // attribute
+ encode_uint32(ns_android_string, &p_manifest[ofs + 36]); // ns
+ encode_uint32(attr_name_string, &p_manifest[ofs + 40]); // 'name'
+ encode_uint32(perm_string, &p_manifest[ofs + 44]); // raw_value
+ encode_uint16(8, &p_manifest[ofs + 48]); // typedvalue_size
+ p_manifest[ofs + 50] = 0; // typedvalue_always0
+ p_manifest[ofs + 51] = 0x03; // typedvalue_type (string)
+ encode_uint32(perm_string, &p_manifest[ofs + 52]); // typedvalue reference
+
+ ofs += 56;
+
+ // end tag
+ encode_uint16(0x103, &p_manifest[ofs]); // type
+ encode_uint16(16, &p_manifest[ofs + 2]); // headersize
+ encode_uint32(24, &p_manifest[ofs + 4]); // size
+ encode_uint32(0, &p_manifest[ofs + 8]); // lineno
+ encode_uint32(-1, &p_manifest[ofs + 12]); // comment
+ encode_uint32(-1, &p_manifest[ofs + 16]); // ns
+ encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name
+
+ ofs += 24;
+ }
+
+ // copy footer back in
+ memcpy(&p_manifest[ofs], manifest_end.ptr(), manifest_end.size());
+ }
+ } break;
}
ofs += size;
@@ -806,17 +874,17 @@ class EditorExportAndroid : public EditorExportPlatform {
encode_uint32(ofs, &ret[string_table_begins + i * 4]);
ofs += string_table[i].length() * 2 + 2 + 2;
- //print_line("ofs: "+itos(i)+": "+itos(ofs));
}
+
ret.resize(ret.size() + ofs);
- uint8_t *chars = &ret[ret.size() - ofs];
+ string_data_offset = ret.size() - ofs;
+ uint8_t *chars = &ret[string_data_offset];
for (int i = 0; i < string_table.size(); i++) {
String s = string_table[i];
- //print_line("savint string :"+s);
encode_uint16(s.length(), chars);
chars += 2;
- for (int j = 0; j < s.length(); j++) { //include zero?
+ for (int j = 0; j < s.length(); j++) {
encode_uint16(s[j], chars);
chars += 2;
}
@@ -828,6 +896,7 @@ class EditorExportAndroid : public EditorExportPlatform {
ret.push_back(stable_extra[i]);
}
+ //pad
while (ret.size() % 4)
ret.push_back(0);
@@ -843,6 +912,8 @@ class EditorExportAndroid : public EditorExportPlatform {
encode_uint32(ret.size(), &ret[4]); //update new file size
encode_uint32(new_stable_end - 8, &ret[12]); //update new string table size
+ encode_uint32(string_table.size(), &ret[16]); //update new number of strings
+ encode_uint32(string_data_offset - 8, &ret[28]); //update new string data offset
//print_line("file size: "+itos(ret.size()));
@@ -1083,7 +1154,10 @@ public:
virtual bool poll_devices() {
bool dc = devices_changed;
- devices_changed = false;
+ if (dc) {
+ // don't clear unless we're reporting true, to avoid race
+ devices_changed = false;
+ }
return dc;
}
@@ -1786,9 +1860,9 @@ public:
run_icon->create_from_image(img);
device_lock = Mutex::create();
- device_thread = Thread::create(_device_poll_thread, this);
devices_changed = true;
quit_request = false;
+ device_thread = Thread::create(_device_poll_thread, this);
}
~EditorExportAndroid() {
diff --git a/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png
index 94bc406416..372b763ec5 100644
--- a/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png
+++ b/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png
index ef6fe4e836..c61c440636 100644
--- a/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png
+++ b/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/platform/android/java/res/drawable/icon.png b/platform/android/java/res/drawable/icon.png
index 29c4a7b8fc..6ad9b43117 100644
--- a/platform/android/java/res/drawable/icon.png
+++ b/platform/android/java/res/drawable/icon.png
Binary files differ
diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java
index 90848e6a90..ef798fc790 100644
--- a/platform/android/java/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/src/org/godotengine/godot/Godot.java
@@ -32,6 +32,7 @@ package org.godotengine.godot;
import android.R;
import android.app.Activity;
+import android.content.pm.ConfigurationInfo;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
@@ -246,9 +247,11 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
}
};
- public void onVideoInit(boolean use_gl2) {
+ public void onVideoInit() {
- //mView = new GodotView(getApplication(),io,use_gl2);
+ boolean use_gl3 = getGLESVersionCode() >= 0x00030000;
+
+ //mView = new GodotView(getApplication(),io,use_gl3);
//setContentView(mView);
layout = new FrameLayout(this);
@@ -261,7 +264,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
layout.addView(edittext);
- mView = new GodotView(getApplication(), io, use_gl2, use_32_bits, this);
+ mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, this);
layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
edittext.setView(mView);
io.setEdit(edittext);
@@ -294,7 +297,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
runOnUiThread(new Runnable() {
@Override
public void run() {
- view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/driver/keep_screen_on")));
+ view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
}
});
}
@@ -338,6 +341,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
return Godot._self;
}
+ public int getGLESVersionCode() {
+ ActivityManager am = (ActivityManager)Godot.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
+ ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo();
+ return deviceInfo.reqGlEsVersion;
+ }
+
private String[] getCommandLine() {
InputStream is;
try {
diff --git a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java
index afe5f81b6d..5d94e77cd7 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java
@@ -66,7 +66,6 @@ abstract public class ConsumeTask {
}
final String token = _token;
new AsyncTask<String, String, String>() {
-
@Override
protected String doInBackground(String... params) {
try {
@@ -89,7 +88,6 @@ abstract public class ConsumeTask {
error(param);
}
}
-
}
.execute();
}
diff --git a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
index 766989f953..aaf18c74bf 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
@@ -58,17 +58,15 @@ abstract public class HandlePurchaseTask {
public void handlePurchaseRequest(int resultCode, Intent data) {
//Log.d("XXX", "Handling purchase response");
- //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
- PaymentsCache pc = new PaymentsCache(context);
-
- String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
- //Log.d("XXX", "Purchase data:" + purchaseData);
- String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
- //Log.d("XXX", "Purchase signature:" + dataSignature);
-
if (resultCode == Activity.RESULT_OK) {
-
try {
+ //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
+ PaymentsCache pc = new PaymentsCache(context);
+
+ String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
+ //Log.d("XXX", "Purchase data:" + purchaseData);
+ String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
+ //Log.d("XXX", "Purchase signature:" + dataSignature);
//Log.d("SARLANGA", purchaseData);
JSONObject jo = new JSONObject(purchaseData);
diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
index b7bf2362cc..d4c7380424 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
@@ -113,7 +113,6 @@ public class PaymentsManager {
public void requestPurchase(final String sku, String transactionId) {
new PurchaseTask(mService, Godot.getInstance()) {
-
@Override
protected void error(String message) {
godotPaymentV3.callbackFail(message);
@@ -128,7 +127,6 @@ public class PaymentsManager {
protected void alreadyOwned() {
godotPaymentV3.callbackAlreadyOwned(sku);
}
-
}
.purchase(sku, transactionId);
}
@@ -139,7 +137,6 @@ public class PaymentsManager {
public void consumeUnconsumedPurchases() {
new ReleaseAllConsumablesTask(mService, activity) {
-
@Override
protected void success(String sku, String receipt, String signature, String token) {
godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
@@ -208,14 +205,12 @@ public class PaymentsManager {
public void processPurchaseResponse(int resultCode, Intent data) {
new HandlePurchaseTask(activity) {
-
@Override
protected void success(final String sku, final String signature, final String ticket) {
godotPaymentV3.callbackSuccess(ticket, signature, sku);
if (auto_consume) {
new ConsumeTask(mService, activity) {
-
@Override
protected void success(String ticket) {
}
@@ -245,12 +240,10 @@ public class PaymentsManager {
public void validatePurchase(String purchaseToken, final String sku) {
new ValidateTask(activity, godotPaymentV3) {
-
@Override
protected void success() {
new ConsumeTask(mService, activity) {
-
@Override
protected void success(String ticket) {
godotPaymentV3.callbackSuccess(ticket, null, sku);
@@ -283,7 +276,6 @@ public class PaymentsManager {
public void consume(final String sku) {
new ConsumeTask(mService, activity) {
-
@Override
protected void success(String ticket) {
godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
diff --git a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
index e00e37f9d1..eccc6f671b 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
@@ -88,7 +88,6 @@ abstract public class ReleaseAllConsumablesTask {
String signature = mySignatures.get(i);
//Log.d("godot", "A punto de consumir un item con token:" + token + "\n" + receipt);
new GenericConsumeTask(context, mService, sku, receipt, signature, token) {
-
@Override
public void onSuccess(String sku, String receipt, String signature, String token) {
ReleaseAllConsumablesTask.this.success(sku, receipt, signature, token);
diff --git a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java
index 1eb9d001e0..0626e50bb1 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java
@@ -63,7 +63,6 @@ abstract public class ValidateTask {
public void validatePurchase(final String sku) {
new AsyncTask<String, String, String>() {
-
private ProgressDialog dialog;
@Override
@@ -113,7 +112,6 @@ abstract public class ValidateTask {
error(e.getMessage());
}
}
-
}
.execute();
}
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index 579c06f76b..e6240ad9e9 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -614,6 +614,7 @@ static jmethodID _hideKeyboard = 0;
static jmethodID _setScreenOrientation = 0;
static jmethodID _getUniqueID = 0;
static jmethodID _getSystemDir = 0;
+static jmethodID _getGLESVersionCode = 0;
static jmethodID _playVideo = 0;
static jmethodID _isVideoPlaying = 0;
static jmethodID _pauseVideo = 0;
@@ -685,6 +686,11 @@ static String _get_system_dir(int p_dir) {
return String(env->GetStringUTFChars(s, NULL));
}
+static int _get_gles_version_code() {
+ JNIEnv *env = ThreadAndroid::get_env();
+ return env->CallIntMethod(_godot_instance, _getGLESVersionCode);
+}
+
static void _hide_vk() {
JNIEnv *env = ThreadAndroid::get_env();
@@ -764,9 +770,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
godot_io = gob;
- _on_video_init = env->GetMethodID(cls, "onVideoInit", "(Z)V");
+ _on_video_init = env->GetMethodID(cls, "onVideoInit", "()V");
_setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V");
_alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
+ _getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I");
jclass clsio = env->FindClass("org/godotengine/godot/Godot");
if (cls) {
@@ -800,16 +807,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
AudioDriverAndroid::setup(gob);
}
- os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion);
+ os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion);
os_android->set_need_reload_hooks(p_need_reload_hook);
char wd[500];
getcwd(wd, 500);
- //video driver is determined here, because once initialized, it can't be changed
- // String vd = ProjectSettings::get_singleton()->get("display/driver");
-
- env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean) true);
+ env->CallVoidMethod(_godot_instance, _on_video_init);
}
static void _initialize_java_modules() {
diff --git a/platform/android/logo.png b/platform/android/logo.png
index fcf684c026..ba2a0e366a 100644
--- a/platform/android/logo.png
+++ b/platform/android/logo.png
Binary files differ
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index fc41adeb76..cc512263bc 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -32,6 +32,7 @@
#include "core/io/file_access_buffered_fa.h"
#include "core/project_settings.h"
+#include "drivers/gles2/rasterizer_gles2.h"
#include "drivers/gles3/rasterizer_gles3.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
@@ -123,15 +124,28 @@ void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
gl_extensions = p_gl_extensions;
}
+int OS_Android::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- use_gl2 = p_video_driver != 1;
+ bool use_gl3 = get_gl_version_code_func() >= 0x00030000;
+ use_gl3 = use_gl3 && (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3");
+ use_gl2 = !use_gl3;
if (gfx_init_func)
gfx_init_func(gfx_init_ud, use_gl2);
- RasterizerGLES3::register_config();
- RasterizerGLES3::make_current();
+ if (use_gl2) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ video_driver_index = VIDEO_DRIVER_GLES2;
+ } else {
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
+ video_driver_index = VIDEO_DRIVER_GLES3;
+ }
visual_server = memnew(VisualServerRaster);
/* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
@@ -684,7 +698,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) {
return false;
}
-OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) {
+OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) {
use_apk_expansion = p_use_apk_expansion;
default_videomode.width = 800;
@@ -706,6 +720,7 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI
get_screen_dpi_func = p_get_screen_dpi_func;
get_unique_id_func = p_get_unique_id;
get_system_dir_func = p_get_sdir_func;
+ get_gl_version_code_func = p_get_gl_version_func;
video_play_func = p_video_play_func;
video_is_playing_func = p_video_is_playing_func;
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index d2457e538d..c4220906a3 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -58,6 +58,7 @@ typedef void (*ShowVirtualKeyboardFunc)(const String &);
typedef void (*HideVirtualKeyboardFunc)();
typedef void (*SetScreenOrientationFunc)(int);
typedef String (*GetSystemDirFunc)(int);
+typedef int (*GetGLVersionCodeFunc)();
typedef void (*VideoPlayFunc)(const String &);
typedef bool (*VideoIsPlayingFunc)();
@@ -126,6 +127,7 @@ private:
SetScreenOrientationFunc set_screen_orientation_func;
GetUniqueIDFunc get_unique_id_func;
GetSystemDirFunc get_system_dir_func;
+ GetGLVersionCodeFunc get_gl_version_code_func;
VideoPlayFunc video_play_func;
VideoIsPlayingFunc video_is_playing_func;
@@ -135,6 +137,7 @@ private:
AlertFunc alert_func;
//power_android *power_manager;
+ int video_driver_index;
public:
// functions used by main to initialize/deintialize the OS
@@ -144,6 +147,8 @@ public:
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
+ virtual int get_current_video_driver() const;
+
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -239,7 +244,7 @@ public:
void joy_connection_changed(int p_device, bool p_connected, String p_name);
virtual bool _check_internal_feature_support(const String &p_feature);
- OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion);
+ OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion);
~OS_Android();
};
diff --git a/platform/android/run_icon.png b/platform/android/run_icon.png
index e53f8e9da5..b687c9ac31 100644
--- a/platform/android/run_icon.png
+++ b/platform/android/run_icon.png
Binary files differ
diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp
index 278a994c54..1f901c4919 100644
--- a/platform/haiku/audio_driver_media_kit.cpp
+++ b/platform/haiku/audio_driver_media_kit.cpp
@@ -43,7 +43,7 @@ Error AudioDriverMediaKit::init() {
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
- int latency = GLOBAL_DEF("audio/output_latency", 25);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", 25);
buffer_size = next_power_of_2(latency * mix_rate / 1000);
samples_in = memnew_arr(int32_t, buffer_size * channels);
diff --git a/platform/haiku/logo.png b/platform/haiku/logo.png
index d5d98e4cc6..a2d8e242a6 100644
--- a/platform/haiku/logo.png
+++ b/platform/haiku/logo.png
Binary files differ
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index 97fab5ca0d..209cb5cec4 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -80,6 +80,10 @@ const char *OS_Haiku::get_video_driver_name(int p_driver) const {
return "GLES3";
}
+int OS_Haiku::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
main_loop = NULL;
current_video_mode = p_desired;
@@ -124,6 +128,8 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p
}
*/
+ video_driver_index = p_video_driver;
+
input = memnew(InputDefault);
window->SetInput(input);
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index 615ae682ef..13d4420bde 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -51,6 +51,7 @@ private:
Rasterizer *rasterizer;
VisualServer *visual_server;
VideoMode current_video_mode;
+ int video_driver_index;
PowerHaiku *power_manager;
#ifdef MEDIA_KIT_ENABLED
@@ -66,6 +67,7 @@ private:
protected:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
+ virtual int get_current_video_driver() const;
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
virtual void finalize();
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index dd5ce4ab10..cc4985eb0c 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -643,7 +643,7 @@ static int frame_count = 0;
view_controller.view = glView;
window.rootViewController = view_controller;
- _set_keep_screen_on(bool(GLOBAL_DEF("display/window/keep_screen_on", true)) ? YES : NO);
+ _set_keep_screen_on(bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)) ? YES : NO);
glView.useCADisplayLink =
bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
printf("cadisaplylink: %d", glView.useCADisplayLink);
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 25674c2b47..b13a1e9643 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -47,8 +47,12 @@ def configure(env):
if (env["target"].startswith("release")):
env.Append(CPPFLAGS=['-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1'])
- env.Append(CPPFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations'])
- env.Append(LINKFLAGS=['-O2'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Append(CPPFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations'])
+ env.Append(LINKFLAGS=['-O2'])
+ else: #optimize for size
+ env.Append(CPPFLAGS=['-Os', '-ftree-vectorize'])
+ env.Append(LINKFLAGS=['-Os'])
if env["target"] == "release_debug":
env.Append(CPPFLAGS=['-DDEBUG_ENABLED'])
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index 57ff79f7bc..e210bfb862 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -139,7 +139,6 @@ Error GameCenter::post_score(Variant p_score) {
[GKScore reportScores:@[ reporter ]
withCompletionHandler:^(NSError *error) {
-
Dictionary ret;
ret["type"] = "post_score";
if (error == nil) {
@@ -177,7 +176,6 @@ Error GameCenter::award_achievement(Variant p_params) {
[GKAchievement reportAchievements:@[ achievement ]
withCompletionHandler:^(NSError *error) {
-
Dictionary ret;
ret["type"] = "award_achievement";
if (error == nil) {
@@ -196,7 +194,6 @@ Error GameCenter::award_achievement(Variant p_params) {
void GameCenter::request_achievement_descriptions() {
[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
-
Dictionary ret;
ret["type"] = "achievement_descriptions";
if (error == nil) {
@@ -252,7 +249,6 @@ void GameCenter::request_achievement_descriptions() {
void GameCenter::request_achievements() {
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
-
Dictionary ret;
ret["type"] = "achievements";
if (error == nil) {
@@ -347,7 +343,6 @@ Error GameCenter::request_identity_verification_signature() {
GKLocalPlayer *player = [GKLocalPlayer localPlayer];
[player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
-
Dictionary ret;
ret["type"] = "identity_verification_signature";
if (error == nil) {
diff --git a/platform/iphone/logo.png b/platform/iphone/logo.png
index 8dd718524c..405b6f93ca 100644
--- a/platform/iphone/logo.png
+++ b/platform/iphone/logo.png
Binary files differ
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index f618c80a77..a4538a6673 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -64,11 +64,6 @@ OSIPhone *OSIPhone::get_singleton() {
return (OSIPhone *)OS::get_singleton();
};
-uint8_t OSIPhone::get_orientations() const {
-
- return supported_orientations;
-};
-
extern int gl_view_base_fb; // from gl_view.mm
void OSIPhone::set_data_dir(String p_dir) {
@@ -98,13 +93,13 @@ void OSIPhone::initialize_core() {
set_data_dir(data_dir);
};
+int OSIPhone::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- supported_orientations = 0;
- supported_orientations |= ((GLOBAL_DEF("video_mode/allow_horizontal", true) ? 1 : 0) << LandscapeLeft);
- supported_orientations |= ((GLOBAL_DEF("video_mode/allow_horizontal_flipped", false) ? 1 : 0) << LandscapeRight);
- supported_orientations |= ((GLOBAL_DEF("video_mode/allow_vertical", false) ? 1 : 0) << PortraitDown);
- supported_orientations |= ((GLOBAL_DEF("video_mode/allow_vertical_flipped", false) ? 1 : 0) << PortraitUp);
+ video_driver_index = p_video_driver; //this may be misleading
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 7d73a6fe5c..db2912ad93 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -47,14 +47,6 @@
class OSIPhone : public OS_Unix {
-public:
- enum Orientations {
- PortraitDown,
- PortraitUp,
- LandscapeLeft,
- LandscapeRight,
- };
-
private:
enum {
MAX_MOUSE_COUNT = 8,
@@ -64,8 +56,6 @@ private:
static HashMap<String, void *> dynamic_symbol_lookup_table;
friend void register_dynamic_symbol(char *name, void *address);
- uint8_t supported_orientations;
-
VisualServer *visual_server;
AudioDriverCoreAudio audio_driver;
@@ -87,6 +77,8 @@ private:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
+ virtual int get_current_video_driver() const;
+
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -122,6 +114,8 @@ private:
int virtual_keyboard_height;
+ int video_driver_index;
+
public:
bool iterate();
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index cdaae0cb81..f75f0fd812 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -83,51 +83,36 @@ int add_cmdline(int p_argc, char **p_args) {
printf("*********** did receive memory warning!\n");
};
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)p_orientation {
-
- if (/*OSIPhone::get_singleton() == NULL*/ TRUE) {
-
- printf("checking on info.plist\n");
- NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"];
- switch (p_orientation) {
-
- case UIInterfaceOrientationLandscapeLeft:
- return [arr indexOfObject:@"UIInterfaceOrientationLandscapeLeft"] != NSNotFound ? YES : NO;
-
- case UIInterfaceOrientationLandscapeRight:
- return [arr indexOfObject:@"UIInterfaceOrientationLandscapeRight"] != NSNotFound ? YES : NO;
-
- case UIInterfaceOrientationPortrait:
- return [arr indexOfObject:@"UIInterfaceOrientationPortrait"] != NSNotFound ? YES : NO;
-
- case UIInterfaceOrientationPortraitUpsideDown:
- return [arr indexOfObject:@"UIInterfaceOrientationPortraitUpsideDown"] != NSNotFound ? YES : NO;
-
- default:
- return NO;
- }
- };
-
- uint8_t supported = OSIPhone::get_singleton()->get_orientations();
- switch (p_orientation) {
-
- case UIInterfaceOrientationLandscapeLeft:
- return supported & (1 << OSIPhone::LandscapeLeft) ? YES : NO;
-
- case UIInterfaceOrientationLandscapeRight:
- return supported & (1 << OSIPhone::LandscapeRight) ? YES : NO;
-
- case UIInterfaceOrientationPortrait:
- return supported & (1 << OSIPhone::PortraitDown) ? YES : NO;
-
- case UIInterfaceOrientationPortraitUpsideDown:
- return supported & (1 << OSIPhone::PortraitUp) ? YES : NO;
-
+- (BOOL)shouldAutorotate {
+ switch (OS::get_singleton()->get_screen_orientation()) {
+ case OS::SCREEN_SENSOR:
+ case OS::SCREEN_SENSOR_LANDSCAPE:
+ case OS::SCREEN_SENSOR_PORTRAIT:
+ return YES;
default:
return NO;
}
};
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
+ switch (OS::get_singleton()->get_screen_orientation()) {
+ case OS::SCREEN_PORTRAIT:
+ return UIInterfaceOrientationMaskPortrait;
+ case OS::SCREEN_REVERSE_LANDSCAPE:
+ return UIInterfaceOrientationMaskLandscapeRight;
+ case OS::SCREEN_REVERSE_PORTRAIT:
+ return UIInterfaceOrientationMaskPortraitUpsideDown;
+ case OS::SCREEN_SENSOR_LANDSCAPE:
+ return UIInterfaceOrientationMaskLandscape;
+ case OS::SCREEN_SENSOR_PORTRAIT:
+ return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
+ case OS::SCREEN_SENSOR:
+ return UIInterfaceOrientationMaskAll;
+ case OS::SCREEN_LANDSCAPE:
+ return UIInterfaceOrientationMaskLandscapeLeft;
+ }
+};
+
- (BOOL)prefersStatusBarHidden {
return YES;
}
diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.inc
index 4edca63c6d..dc8d67d52b 100644
--- a/platform/javascript/dom_keys.h
+++ b/platform/javascript/dom_keys.inc
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* dom_keys.h */
+/* dom_keys.inc */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,9 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef DOM_KEYS_H
-#define DOM_KEYS_H
-
#include "os/keyboard.h"
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value
@@ -295,8 +292,8 @@ int dom2godot_scancode(int dom_keycode) {
//case DOM_VK_SELECT: return KEY_UNKNOWN;
- case DOM_VK_PRINTSCREEN: // this is the usual printScreen key
- case DOM_VK_PRINT: // maybe for alt+printScreen or physical printers?
+ case DOM_VK_PRINTSCREEN:
+ case DOM_VK_PRINT:
return KEY_PRINT;
//case DOM_VK_EXECUTE: return KEY_UNKNOWN;
@@ -311,11 +308,11 @@ int dom2godot_scancode(int dom_keycode) {
case DOM_VK_SLEEP:
return KEY_STANDBY;
- // these are numpad keys according to MDN
+ // Numpad keys
case DOM_VK_MULTIPLY: return KEY_KP_MULTIPLY;
case DOM_VK_ADD: return KEY_KP_ADD;
case DOM_VK_SEPARATOR:
- return KEY_KP_PERIOD; // good enough?
+ return KEY_KP_PERIOD; // Good enough?
case DOM_VK_SUBTRACT: return KEY_KP_SUBTRACT;
case DOM_VK_DECIMAL: return KEY_KP_PERIOD;
case DOM_VK_DIVIDE:
@@ -376,10 +373,8 @@ int dom2godot_scancode(int dom_keycode) {
case DOM_VK_QUOTE:
return KEY_APOSTROPHE;
- // rest is OEM/unusual
+ // The rest is OEM/unusual.
default: return KEY_UNKNOWN;
};
}
-
-#endif
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 68a2d72464..3829e8d406 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -28,17 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "emscripten.h"
#include "io/resource_loader.h"
#include "main/main.h"
#include "os_javascript.h"
-OS_JavaScript *os = NULL;
-
-static void main_loop() {
-
- os->main_loop_iterate();
-}
+#include <emscripten/emscripten.h>
extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
@@ -46,18 +40,18 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
if (!idbfs_err.empty()) {
print_line("IndexedDB not available: " + idbfs_err);
}
- os->set_idbfs_available(idbfs_err.empty());
- // Ease up compatibility
+ OS_JavaScript *os = OS_JavaScript::get_singleton();
+ os->set_idb_available(idbfs_err.empty());
+ // Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
Main::start();
- os->main_loop_begin();
- emscripten_set_main_loop(main_loop, 0, false);
+ os->run_async();
}
int main(int argc, char *argv[]) {
- // sync from persistent state into memory and then
- // run the 'main_after_fs_sync' function
+ // Sync from persistent state into memory and then
+ // run the 'main_after_fs_sync' function.
/* clang-format off */
EM_ASM(
FS.mkdir('/userfs');
@@ -68,9 +62,10 @@ int main(int argc, char *argv[]) {
);
/* clang-format on */
- os = new OS_JavaScript(argv[0], NULL);
- Error err = Main::setup(argv[0], argc - 1, &argv[1]);
+ new OS_JavaScript(argc, argv);
+ // TODO: Check error return value.
+ Main::setup(argv[0], argc - 1, &argv[1]);
return 0;
- // continued async in main_after_fs_sync() from syncfs() callback
+ // Continued async in main_after_fs_sync() from the syncfs() callback.
}
diff --git a/platform/javascript/logo.png b/platform/javascript/logo.png
index ce911180ac..36832d93ba 100644
--- a/platform/javascript/logo.png
+++ b/platform/javascript/logo.png
Binary files differ
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 6c6e4d2d1c..b9d586e233 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -30,290 +30,196 @@
#include "os_javascript.h"
-#include "core/engine.h"
-#include "core/io/file_access_buffered_fa.h"
-#include "dom_keys.h"
-#include "drivers/gles2/rasterizer_gles2.h"
-#include "drivers/gles3/rasterizer_gles3.h"
-#include "drivers/unix/dir_access_unix.h"
-#include "drivers/unix/file_access_unix.h"
+#include "gles2/rasterizer_gles2.h"
+#include "gles3/rasterizer_gles3.h"
+#include "io/file_access_buffered_fa.h"
#include "main/main.h"
#include "servers/visual/visual_server_raster.h"
+#include "unix/dir_access_unix.h"
+#include "unix/file_access_unix.h"
#include <emscripten.h>
#include <stdlib.h>
+#include "dom_keys.inc"
+
#define DOM_BUTTON_LEFT 0
#define DOM_BUTTON_MIDDLE 1
#define DOM_BUTTON_RIGHT 2
+#define DOM_BUTTON_XBUTTON1 3
+#define DOM_BUTTON_XBUTTON2 4
-template <typename T>
-static void dom2godot_mod(T emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
-
- godot_event->set_shift(emscripten_event_ptr->shiftKey);
- godot_event->set_alt(emscripten_event_ptr->altKey);
- godot_event->set_control(emscripten_event_ptr->ctrlKey);
- godot_event->set_metakey(emscripten_event_ptr->metaKey);
-}
-
-int OS_JavaScript::get_video_driver_count() const {
+// Window (canvas)
- return VIDEO_DRIVER_MAX;
-}
-
-const char *OS_JavaScript::get_video_driver_name(int p_driver) const {
-
- switch (p_driver) {
- case VIDEO_DRIVER_GLES3:
- return "GLES3";
- case VIDEO_DRIVER_GLES2:
- return "GLES2";
- }
- ERR_EXPLAIN("Invalid video driver index " + itos(p_driver));
- ERR_FAIL_V(NULL);
-}
-
-int OS_JavaScript::get_audio_driver_count() const {
-
- return 1;
-}
-
-const char *OS_JavaScript::get_audio_driver_name(int p_driver) const {
+static void focus_canvas() {
- return "JavaScript";
+ /* clang-format off */
+ EM_ASM(
+ Module.canvas.focus();
+ );
+ /* clang-format on */
}
-void OS_JavaScript::initialize_core() {
+static bool is_canvas_focused() {
- OS_Unix::initialize_core();
- FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES);
+ /* clang-format off */
+ return EM_ASM_INT_V(
+ return document.activeElement == Module.canvas;
+ );
+ /* clang-format on */
}
-static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent *ui_event, void *user_data) {
+static bool cursor_inside_canvas = true;
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false);
+EM_BOOL OS_JavaScript::browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data) {
- OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data);
// The order of the fullscreen change event and the window size change
- // event varies, even within just one browser, so defer handling
- os->request_canvas_size_adjustment();
+ // event varies, even within just one browser, so defer handling.
+ get_singleton()->canvas_size_adjustment_requested = true;
return false;
}
-static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFullscreenChangeEvent *event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_FULLSCREENCHANGE, false);
+EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
- OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data);
- String id = String::utf8(event->id);
- // empty id is canvas
- if (id.empty() || id == "canvas") {
-
- OS::VideoMode vm = os->get_video_mode();
- // this event property is the only reliable information on
- // browser fullscreen state
- vm.fullscreen = event->isFullscreen;
- os->set_video_mode(vm);
- os->request_canvas_size_adjustment();
+ OS_JavaScript *os = get_singleton();
+ // Empty ID is canvas.
+ String target_id = String::utf8(p_event->id);
+ if (target_id.empty() || target_id == "canvas") {
+ // This event property is the only reliable data on
+ // browser fullscreen state.
+ os->video_mode.fullscreen = p_event->isFullscreen;
+ os->canvas_size_adjustment_requested = true;
}
return false;
}
-static InputDefault *_input;
-
-static bool is_canvas_focused() {
+void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
- /* clang-format off */
- return EM_ASM_INT_V(
- return document.activeElement == Module.canvas;
- );
- /* clang-format on */
+ video_mode = p_video_mode;
}
-static void focus_canvas() {
+OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const {
- /* clang-format off */
- EM_ASM(
- Module.canvas.focus();
- );
- /* clang-format on */
+ return video_mode;
}
-static bool _cursor_inside_canvas = true;
-
-static bool is_cursor_inside_canvas() {
+Size2 OS_JavaScript::get_screen_size(int p_screen) const {
- return _cursor_inside_canvas;
+ EmscriptenFullscreenChangeEvent ev;
+ EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev);
+ ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2());
+ return Size2(ev.screenWidth, ev.screenHeight);
}
-static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEDOWN && event_type != EMSCRIPTEN_EVENT_MOUSEUP, false);
-
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_pressed(event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
- ev->set_position(Point2(mouse_event->canvasX, mouse_event->canvasY));
- ev->set_global_position(ev->get_position());
- dom2godot_mod(mouse_event, ev);
-
- switch (mouse_event->button) {
- case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break;
- case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break;
- case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break;
- default: return false;
- }
+void OS_JavaScript::set_window_size(const Size2 p_size) {
- int mask = _input->get_mouse_button_mask();
- int button_flag = 1 << (ev->get_button_index() - 1);
- if (ev->is_pressed()) {
- // Since the event is consumed, focus manually. The containing iframe,
- // if used, may not have focus yet, so focus even if already focused.
- focus_canvas();
- mask |= button_flag;
- } else if (mask & button_flag) {
- mask &= ~button_flag;
+ windowed_size = p_size;
+ if (is_window_fullscreen()) {
+ window_maximized = false;
+ set_window_fullscreen(false);
+ } else if (is_window_maximized()) {
+ set_window_maximized(false);
} else {
- // release event, but press was outside the canvas, so ignore
- return false;
+ video_mode.width = p_size.x;
+ video_mode.height = p_size.y;
+ emscripten_set_canvas_size(p_size.x, p_size.y);
}
- ev->set_button_mask(mask);
-
- _input->parse_input_event(ev);
- // Prevent multi-click text selection and wheel-click scrolling anchor.
- // Context menu is prevented through contextmenu event.
- return true;
}
-static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEMOVE, false);
- OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data);
- int input_mask = _input->get_mouse_button_mask();
- Point2 pos = Point2(mouse_event->canvasX, mouse_event->canvasY);
- // outside the canvas, only read mouse movement if dragging started inside
- // the canvas; imitating desktop app behaviour
- if (!is_cursor_inside_canvas() && !input_mask)
- return false;
-
- Ref<InputEventMouseMotion> ev;
- ev.instance();
- dom2godot_mod(mouse_event, ev);
- ev->set_button_mask(input_mask);
-
- ev->set_position(pos);
- ev->set_global_position(ev->get_position());
-
- ev->set_relative(Vector2(mouse_event->movementX, mouse_event->movementY));
- _input->set_mouse_position(ev->get_position());
- ev->set_speed(_input->get_last_mouse_speed());
+Size2 OS_JavaScript::get_window_size() const {
- _input->parse_input_event(ev);
- // don't suppress mouseover/leave events
- return false;
+ int canvas[3];
+ emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2);
+ return Size2(canvas[0], canvas[1]);
}
-static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel_event, void *user_data) {
+void OS_JavaScript::set_window_maximized(bool p_enabled) {
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_WHEEL, false);
- if (!is_canvas_focused()) {
- if (is_cursor_inside_canvas()) {
- focus_canvas();
- } else {
- return false;
- }
+ window_maximized = p_enabled;
+ if (is_window_fullscreen()) {
+ set_window_fullscreen(false);
+ return;
}
+ // Calling emscripten_enter_soft_fullscreen mutltiple times hides all
+ // page elements except the canvas permanently, so track state.
+ if (p_enabled && !soft_fullscreen_enabled) {
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_button_mask(_input->get_mouse_button_mask());
- ev->set_position(_input->get_mouse_position());
- ev->set_global_position(ev->get_position());
-
- ev->set_shift(_input->is_key_pressed(KEY_SHIFT));
- ev->set_alt(_input->is_key_pressed(KEY_ALT));
- ev->set_control(_input->is_key_pressed(KEY_CONTROL));
- ev->set_metakey(_input->is_key_pressed(KEY_META));
-
- if (wheel_event->deltaY < 0)
- ev->set_button_index(BUTTON_WHEEL_UP);
- else if (wheel_event->deltaY > 0)
- ev->set_button_index(BUTTON_WHEEL_DOWN);
- else if (wheel_event->deltaX > 0)
- ev->set_button_index(BUTTON_WHEEL_LEFT);
- else if (wheel_event->deltaX < 0)
- ev->set_button_index(BUTTON_WHEEL_RIGHT);
- else
- return false;
-
- // Different browsers give wildly different delta values, and we can't
- // interpret deltaMode, so use default value for wheel events' factor
+ EmscriptenFullscreenStrategy strategy;
+ strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
+ strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
+ strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+ strategy.canvasResizedCallback = NULL;
+ emscripten_enter_soft_fullscreen(NULL, &strategy);
+ soft_fullscreen_enabled = true;
+ video_mode.width = get_window_size().width;
+ video_mode.height = get_window_size().height;
+ } else if (!p_enabled) {
- ev->set_pressed(true);
- _input->parse_input_event(ev);
+ emscripten_exit_soft_fullscreen();
+ soft_fullscreen_enabled = false;
+ video_mode.width = windowed_size.width;
+ video_mode.height = windowed_size.height;
+ emscripten_set_canvas_size(video_mode.width, video_mode.height);
+ }
+}
- ev->set_pressed(false);
- _input->parse_input_event(ev);
+bool OS_JavaScript::is_window_maximized() const {
- return true;
+ return window_maximized;
}
-static Point2 _prev_touches[32];
+void OS_JavaScript::set_window_fullscreen(bool p_enabled) {
-static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) {
-
- ERR_FAIL_COND_V(
- event_type != EMSCRIPTEN_EVENT_TOUCHSTART &&
- event_type != EMSCRIPTEN_EVENT_TOUCHEND &&
- event_type != EMSCRIPTEN_EVENT_TOUCHCANCEL,
- false);
+ if (p_enabled == is_window_fullscreen()) {
+ return;
+ }
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- int lowest_id_index = -1;
- for (int i = 0; i < touch_event->numTouches; ++i) {
+ // Just request changes here, if successful, canvas is resized in
+ // _browser_resize_callback or _fullscreen_change_callback.
+ EMSCRIPTEN_RESULT result;
+ if (p_enabled) {
+ if (window_maximized) {
+ // Soft fullsreen during real fulllscreen can cause issues.
+ set_window_maximized(false);
+ window_maximized = true;
+ }
+ EmscriptenFullscreenStrategy strategy;
+ strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
+ strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
+ strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+ strategy.canvasResizedCallback = NULL;
+ emscripten_request_fullscreen_strategy(NULL, false, &strategy);
+ } else {
+ result = emscripten_exit_fullscreen();
+ if (result != EMSCRIPTEN_RESULT_SUCCESS) {
+ ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result));
+ }
+ }
+}
- const EmscriptenTouchPoint &touch = touch_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev->set_index(touch.identifier);
- ev->set_position(Point2(touch.canvasX, touch.canvasY));
- _prev_touches[i] = ev->get_position();
- ev->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
+bool OS_JavaScript::is_window_fullscreen() const {
- _input->parse_input_event(ev);
- }
- return true;
+ return video_mode.fullscreen;
}
-static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) {
+void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_TOUCHMOVE, false);
+ Size2 screen = get_screen_size();
+ p_list->push_back(OS::VideoMode(screen.width, screen.height, true));
+}
- Ref<InputEventScreenDrag> ev;
- ev.instance();
- int lowest_id_index = -1;
- for (int i = 0; i < touch_event->numTouches; ++i) {
+// Keys
- const EmscriptenTouchPoint &touch = touch_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev->set_index(touch.identifier);
- ev->set_position(Point2(touch.canvasX, touch.canvasY));
- Point2 &prev = _prev_touches[i];
- ev->set_relative(ev->get_position() - prev);
- prev = ev->get_position();
+template <typename T>
+static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
- _input->parse_input_event(ev);
- }
- return true;
+ godot_event->set_shift(emscripten_event_ptr->shiftKey);
+ godot_event->set_alt(emscripten_event_ptr->altKey);
+ godot_event->set_control(emscripten_event_ptr->ctrlKey);
+ godot_event->set_metakey(emscripten_event_ptr->metaKey);
}
-static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
+static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
Ref<InputEventKey> ev;
ev.instance();
@@ -322,9 +228,9 @@ static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscri
ev->set_scancode(dom2godot_scancode(emscripten_event->keyCode));
String unicode = String::utf8(emscripten_event->key);
- // check if empty or multi-character (e.g. `CapsLock`)
+ // Check if empty or multi-character (e.g. `CapsLock`).
if (unicode.length() != 1) {
- // might be empty as well, but better than nonsense
+ // Might be empty as well, but better than nonsense.
unicode = String::utf8(emscripten_event->charValue);
}
if (unicode.length() == 1) {
@@ -334,175 +240,115 @@ static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscri
return ev;
}
-static Ref<InputEventKey> deferred_key_event;
-
-static EM_BOOL _keydown_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYDOWN, false);
+EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- Ref<InputEventKey> ev = _setup_key_event(key_event);
+ OS_JavaScript *os = get_singleton();
+ Ref<InputEventKey> ev = setup_key_event(p_event);
ev->set_pressed(true);
if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_scancode())) {
- // defer to keypress event for legacy unicode retrieval
- deferred_key_event = ev;
- return false; // do not suppress keypress event
+ // Defer to keypress event for legacy unicode retrieval.
+ os->deferred_key_event = ev;
+ // Do not suppress keypress event.
+ return false;
}
- _input->parse_input_event(ev);
+ os->input->parse_input_event(ev);
return true;
}
-static EM_BOOL _keypress_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYPRESS, false);
+EM_BOOL OS_JavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- deferred_key_event->set_unicode(key_event->charCode);
- _input->parse_input_event(deferred_key_event);
+ OS_JavaScript *os = get_singleton();
+ os->deferred_key_event->set_unicode(p_event->charCode);
+ os->input->parse_input_event(os->deferred_key_event);
return true;
}
-static EM_BOOL _keyup_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) {
-
- ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYUP, false);
+EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- Ref<InputEventKey> ev = _setup_key_event(key_event);
+ Ref<InputEventKey> ev = setup_key_event(p_event);
ev->set_pressed(false);
- _input->parse_input_event(ev);
+ get_singleton()->input->parse_input_event(ev);
return ev->get_scancode() != KEY_UNKNOWN && ev->get_scancode() != 0;
}
-static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_event, void *p_user) {
- OS_JavaScript *os = (OS_JavaScript *)OS::get_singleton();
- if (os) {
- return os->joy_connection_changed(p_type, p_event);
- }
- return false;
+// Mouse
+
+Point2 OS_JavaScript::get_mouse_position() const {
+
+ return input->get_mouse_position();
}
-extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int notif) {
+int OS_JavaScript::get_mouse_button_state() const {
- if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
- _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
- }
- OS_JavaScript::get_singleton()->get_main_loop()->notification(notif);
+ return input->get_mouse_button_mask();
}
-Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
+EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
- print_line("Init OS");
+ OS_JavaScript *os = get_singleton();
- EmscriptenWebGLContextAttributes attributes;
- emscripten_webgl_init_context_attributes(&attributes);
- attributes.alpha = false;
- attributes.antialias = false;
- ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
- switch (p_video_driver) {
- case VIDEO_DRIVER_GLES3:
- attributes.majorVersion = 2;
- RasterizerGLES3::register_config();
- RasterizerGLES3::make_current();
- break;
- case VIDEO_DRIVER_GLES2:
- attributes.majorVersion = 1;
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- break;
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
+ ev->set_position(Point2(p_event->canvasX, p_event->canvasY));
+ ev->set_global_position(ev->get_position());
+ dom2godot_mod(p_event, ev);
+ switch (p_event->button) {
+ case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break;
+ case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break;
+ case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break;
+ case DOM_BUTTON_XBUTTON1: ev->set_button_index(BUTTON_XBUTTON1); break;
+ case DOM_BUTTON_XBUTTON2: ev->set_button_index(BUTTON_XBUTTON2); break;
+ default: return false;
}
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes);
- ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available");
- ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE);
- video_mode = p_desired;
- // can't fulfil fullscreen request due to browser security
- video_mode.fullscreen = false;
- /* clang-format off */
- if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) {
- /* clang-format on */
- set_window_size(Size2(video_mode.width, video_mode.height));
+ int mask = os->input->get_mouse_button_mask();
+ int button_flag = 1 << (ev->get_button_index() - 1);
+ if (ev->is_pressed()) {
+ // Since the event is consumed, focus manually. The containing iframe,
+ // if exists, may not have focus yet, so focus even if already focused.
+ focus_canvas();
+ mask |= button_flag;
+ } else if (mask & button_flag) {
+ mask &= ~button_flag;
} else {
- set_window_size(get_window_size());
+ // Received release event, but press was outside the canvas, so ignore.
+ return false;
}
+ ev->set_button_mask(mask);
- char locale_ptr[16];
- /* clang-format off */
- EM_ASM_ARGS({
- stringToUTF8(Module.locale, $0, 16);
- }, locale_ptr);
- /* clang-format on */
- setenv("LANG", locale_ptr, true);
-
- print_line("Init Audio");
-
- AudioDriverManager::initialize(p_audio_driver);
-
- print_line("Init VS");
-
- visual_server = memnew(VisualServerRaster());
- // visual_server->cursor_set_visible(false, 0);
-
- print_line("Init Physicsserver");
-
- input = memnew(InputDefault);
- _input = input;
-
-#define EM_CHECK(ev) \
- if (result != EMSCRIPTEN_RESULT_SUCCESS) \
- ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result))
-#define SET_EM_CALLBACK(target, ev, cb) \
- result = emscripten_set_##ev##_callback(target, this, true, &cb); \
- EM_CHECK(ev)
-#define SET_EM_CALLBACK_NODATA(ev, cb) \
- result = emscripten_set_##ev##_callback(NULL, true, &cb); \
- EM_CHECK(ev)
-
- EMSCRIPTEN_RESULT result;
- SET_EM_CALLBACK("#window", mousemove, _mousemove_callback)
- SET_EM_CALLBACK("#canvas", mousedown, _mousebutton_callback)
- SET_EM_CALLBACK("#window", mouseup, _mousebutton_callback)
- SET_EM_CALLBACK("#window", wheel, _wheel_callback)
- SET_EM_CALLBACK("#window", touchstart, _touchpress_callback)
- SET_EM_CALLBACK("#window", touchmove, _touchmove_callback)
- SET_EM_CALLBACK("#window", touchend, _touchpress_callback)
- SET_EM_CALLBACK("#window", touchcancel, _touchpress_callback)
- SET_EM_CALLBACK("#canvas", keydown, _keydown_callback)
- SET_EM_CALLBACK("#canvas", keypress, _keypress_callback)
- SET_EM_CALLBACK("#canvas", keyup, _keyup_callback)
- SET_EM_CALLBACK(NULL, resize, _browser_resize_callback)
- SET_EM_CALLBACK(NULL, fullscreenchange, _fullscreen_change_callback)
- SET_EM_CALLBACK_NODATA(gamepadconnected, joy_callback_func)
- SET_EM_CALLBACK_NODATA(gamepaddisconnected, joy_callback_func)
-
-#undef SET_EM_CALLBACK_NODATA
-#undef SET_EM_CALLBACK
-#undef EM_CHECK
-
- visual_server->init();
-
- return OK;
+ os->input->parse_input_event(ev);
+ // Prevent multi-click text selection and wheel-click scrolling anchor.
+ // Context menu is prevented through contextmenu event.
+ return true;
}
-void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
+EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
-}
-
-void OS_JavaScript::delete_main_loop() {
+ OS_JavaScript *os = get_singleton();
- memdelete(main_loop);
-}
+ int input_mask = os->input->get_mouse_button_mask();
+ Point2 pos = Point2(p_event->canvasX, p_event->canvasY);
+ // For motion outside the canvas, only read mouse movement if dragging
+ // started inside the canvas; imitating desktop app behaviour.
+ if (!cursor_inside_canvas && !input_mask)
+ return false;
-void OS_JavaScript::finalize() {
+ Ref<InputEventMouseMotion> ev;
+ ev.instance();
+ dom2godot_mod(p_event, ev);
+ ev->set_button_mask(input_mask);
- memdelete(input);
-}
+ ev->set_position(pos);
+ ev->set_global_position(ev->get_position());
-void OS_JavaScript::alert(const String &p_alert, const String &p_title) {
+ ev->set_relative(Vector2(p_event->movementX, p_event->movementY));
+ os->input->set_mouse_position(ev->get_position());
+ ev->set_speed(os->input->get_last_mouse_speed());
- /* clang-format off */
- EM_ASM_({
- window.alert(UTF8ToString($0));
- }, p_alert.utf8().get_data());
- /* clang-format on */
+ os->input->parse_input_event(ev);
+ // Don't suppress mouseover/-leave events.
+ return false;
}
static const char *godot2dom_cursor(OS::CursorShape p_shape) {
@@ -530,7 +376,7 @@ static const char *godot2dom_cursor(OS::CursorShape p_shape) {
}
}
-void OS_JavaScript::set_css_cursor(const char *p_cursor) {
+static void set_css_cursor(const char *p_cursor) {
/* clang-format off */
EM_ASM_({
@@ -539,7 +385,7 @@ void OS_JavaScript::set_css_cursor(const char *p_cursor) {
/* clang-format on */
}
-const char *OS_JavaScript::get_css_cursor() const {
+static const char *get_css_cursor() {
char cursor[16];
/* clang-format off */
@@ -550,9 +396,20 @@ const char *OS_JavaScript::get_css_cursor() const {
return cursor;
}
+void OS_JavaScript::set_cursor_shape(CursorShape p_shape) {
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ cursor_shape = p_shape;
+ if (get_mouse_mode() != MOUSE_MODE_HIDDEN)
+ set_css_cursor(godot2dom_cursor(cursor_shape));
+}
+
+void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+}
+
void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) {
- ERR_FAIL_INDEX(p_mode, MOUSE_MODE_CONFINED + 1);
ERR_EXPLAIN("MOUSE_MODE_CONFINED is not supported for the HTML5 platform");
ERR_FAIL_COND(p_mode == MOUSE_MODE_CONFINED);
if (p_mode == get_mouse_mode())
@@ -580,190 +437,308 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) {
OS::MouseMode OS_JavaScript::get_mouse_mode() const {
- if (!strcmp(get_css_cursor(), "none"))
+ if (String::utf8(get_css_cursor()) == "none")
return MOUSE_MODE_HIDDEN;
EmscriptenPointerlockChangeEvent ev;
emscripten_get_pointerlock_status(&ev);
- return ev.isActive && (strcmp(ev.id, "canvas") == 0) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE;
+ return (ev.isActive && String::utf8(ev.id) == "canvas") ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE;
}
-Point2 OS_JavaScript::get_mouse_position() const {
+// Wheel
- return input->get_mouse_position();
-}
+EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) {
-int OS_JavaScript::get_mouse_button_state() const {
+ ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false);
+ if (!is_canvas_focused()) {
+ if (cursor_inside_canvas) {
+ focus_canvas();
+ } else {
+ return false;
+ }
+ }
- return input->get_mouse_button_mask();
-}
+ InputDefault *input = get_singleton()->input;
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ ev->set_button_mask(input->get_mouse_button_mask());
+ ev->set_position(input->get_mouse_position());
+ ev->set_global_position(ev->get_position());
-void OS_JavaScript::set_window_title(const String &p_title) {
+ ev->set_shift(input->is_key_pressed(KEY_SHIFT));
+ ev->set_alt(input->is_key_pressed(KEY_ALT));
+ ev->set_control(input->is_key_pressed(KEY_CONTROL));
+ ev->set_metakey(input->is_key_pressed(KEY_META));
- /* clang-format off */
- EM_ASM_({
- document.title = UTF8ToString($0);
- }, p_title.utf8().get_data());
- /* clang-format on */
-}
+ if (p_event->deltaY < 0)
+ ev->set_button_index(BUTTON_WHEEL_UP);
+ else if (p_event->deltaY > 0)
+ ev->set_button_index(BUTTON_WHEEL_DOWN);
+ else if (p_event->deltaX > 0)
+ ev->set_button_index(BUTTON_WHEEL_LEFT);
+ else if (p_event->deltaX < 0)
+ ev->set_button_index(BUTTON_WHEEL_RIGHT);
+ else
+ return false;
-//interesting byt not yet
-//void set_clipboard(const String& p_text);
-//String get_clipboard() const;
+ // Different browsers give wildly different delta values, and we can't
+ // interpret deltaMode, so use default value for wheel events' factor.
-void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
+ ev->set_pressed(true);
+ input->parse_input_event(ev);
- video_mode = p_video_mode;
+ ev->set_pressed(false);
+ input->parse_input_event(ev);
+
+ return true;
}
-OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const {
+// Touch
- return video_mode;
+bool OS_JavaScript::has_touchscreen_ui_hint() const {
+
+ /* clang-format off */
+ return EM_ASM_INT_V(
+ return 'ontouchstart' in window;
+ );
+ /* clang-format on */
}
-Size2 OS_JavaScript::get_screen_size(int p_screen) const {
+EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
- EmscriptenFullscreenChangeEvent ev;
- EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev);
- ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2());
- return Size2(ev.screenWidth, ev.screenHeight);
-}
+ OS_JavaScript *os = get_singleton();
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ int lowest_id_index = -1;
+ for (int i = 0; i < p_event->numTouches; ++i) {
-void OS_JavaScript::set_window_size(const Size2 p_size) {
+ const EmscriptenTouchPoint &touch = p_event->touches[i];
+ if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
+ lowest_id_index = i;
+ if (!touch.isChanged)
+ continue;
+ ev->set_index(touch.identifier);
+ ev->set_position(Point2(touch.canvasX, touch.canvasY));
+ os->touches[i] = ev->get_position();
+ ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
- windowed_size = p_size;
- if (is_window_fullscreen()) {
- window_maximized = false;
- set_window_fullscreen(false);
- } else if (is_window_maximized()) {
- set_window_maximized(false);
- } else {
- video_mode.width = p_size.x;
- video_mode.height = p_size.y;
- emscripten_set_canvas_size(p_size.x, p_size.y);
+ os->input->parse_input_event(ev);
}
+ return true;
}
-Size2 OS_JavaScript::get_window_size() const {
+EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
- int canvas[3];
- emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2);
- return Size2(canvas[0], canvas[1]);
-}
+ OS_JavaScript *os = get_singleton();
+ Ref<InputEventScreenDrag> ev;
+ ev.instance();
+ int lowest_id_index = -1;
+ for (int i = 0; i < p_event->numTouches; ++i) {
-void OS_JavaScript::set_window_maximized(bool p_enabled) {
+ const EmscriptenTouchPoint &touch = p_event->touches[i];
+ if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
+ lowest_id_index = i;
+ if (!touch.isChanged)
+ continue;
+ ev->set_index(touch.identifier);
+ ev->set_position(Point2(touch.canvasX, touch.canvasY));
+ Point2 &prev = os->touches[i];
+ ev->set_relative(ev->get_position() - prev);
+ prev = ev->get_position();
- window_maximized = p_enabled;
- if (is_window_fullscreen()) {
- set_window_fullscreen(false);
- return;
+ os->input->parse_input_event(ev);
}
- // Calling emscripten_enter_soft_fullscreen mutltiple times hides all
- // page elements except the canvas permanently, so track state
- if (p_enabled && !soft_fs_enabled) {
+ return true;
+}
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
- emscripten_enter_soft_fullscreen(NULL, &strategy);
- soft_fs_enabled = true;
- video_mode.width = get_window_size().width;
- video_mode.height = get_window_size().height;
- } else if (!p_enabled) {
+// Gamepad
- emscripten_exit_soft_fullscreen();
- soft_fs_enabled = false;
- video_mode.width = windowed_size.width;
- video_mode.height = windowed_size.height;
- emscripten_set_canvas_size(video_mode.width, video_mode.height);
+EM_BOOL OS_JavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) {
+
+ InputDefault *input = get_singleton()->input;
+ if (p_event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) {
+
+ String guid = "";
+ if (String::utf8(p_event->mapping) == "standard")
+ guid = "Default HTML5 Gamepad";
+ input->joy_connection_changed(p_event->index, true, String::utf8(p_event->id), guid);
+ } else {
+ input->joy_connection_changed(p_event->index, false, "");
}
+ return true;
}
-void OS_JavaScript::set_window_fullscreen(bool p_enable) {
+void OS_JavaScript::process_joypads() {
- if (p_enable == is_window_fullscreen()) {
- return;
- }
+ int joypad_count = emscripten_get_num_gamepads();
+ for (int joypad = 0; joypad < joypad_count; joypad++) {
+ EmscriptenGamepadEvent state;
+ emscripten_get_gamepad_status(joypad, &state);
+ if (state.connected) {
- // only requesting changes here, if successful, canvas is resized in
- // _browser_resize_callback or _fullscreen_change_callback
- EMSCRIPTEN_RESULT result;
- if (p_enable) {
- if (window_maximized) {
- // soft fs during real fs can cause issues
- set_window_maximized(false);
- window_maximized = true;
- }
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
- emscripten_request_fullscreen_strategy(NULL, false, &strategy);
- } else {
- result = emscripten_exit_fullscreen();
- if (result != EMSCRIPTEN_RESULT_SUCCESS) {
- ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result));
+ int button_count = MIN(state.numButtons, 18);
+ int axis_count = MIN(state.numAxes, 8);
+ for (int button = 0; button < button_count; button++) {
+
+ float value = state.analogButton[button];
+ if (String::utf8(state.mapping) == "standard" && (button == JOY_ANALOG_L2 || button == JOY_ANALOG_R2)) {
+ InputDefault::JoyAxis joy_axis;
+ joy_axis.min = 0;
+ joy_axis.value = value;
+ input->joy_axis(joypad, button, joy_axis);
+ } else {
+ input->joy_button(joypad, button, value);
+ }
+ }
+ for (int axis = 0; axis < axis_count; axis++) {
+
+ InputDefault::JoyAxis joy_axis;
+ joy_axis.min = -1;
+ joy_axis.value = state.axis[axis];
+ input->joy_axis(joypad, axis, joy_axis);
+ }
}
}
}
-bool OS_JavaScript::is_window_fullscreen() const {
+bool OS_JavaScript::is_joy_known(int p_device) {
- return video_mode.fullscreen;
+ return input->is_joy_mapped(p_device);
}
-void OS_JavaScript::request_canvas_size_adjustment() {
+String OS_JavaScript::get_joy_guid(int p_device) const {
- canvas_size_adjustment_requested = true;
+ return input->get_joy_guid_remapped(p_device);
}
-void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
+// Video
- Size2 screen = get_screen_size();
- p_list->push_back(OS::VideoMode(screen.width, screen.height, true));
+int OS_JavaScript::get_video_driver_count() const {
+
+ return VIDEO_DRIVER_MAX;
}
-String OS_JavaScript::get_name() {
+const char *OS_JavaScript::get_video_driver_name(int p_driver) const {
- return "HTML5";
+ switch (p_driver) {
+ case VIDEO_DRIVER_GLES3:
+ return "GLES3";
+ case VIDEO_DRIVER_GLES2:
+ return "GLES2";
+ }
+ ERR_EXPLAIN("Invalid video driver index " + itos(p_driver));
+ ERR_FAIL_V(NULL);
}
-MainLoop *OS_JavaScript::get_main_loop() const {
+// Audio
- return main_loop;
+int OS_JavaScript::get_audio_driver_count() const {
+
+ return 1;
}
-bool OS_JavaScript::can_draw() const {
+const char *OS_JavaScript::get_audio_driver_name(int p_driver) const {
- return true; //always?
+ return "JavaScript";
}
-void OS_JavaScript::set_cursor_shape(CursorShape p_shape) {
+// Lifecycle
+int OS_JavaScript::get_current_video_driver() const {
+ return video_driver_index;
+}
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+void OS_JavaScript::initialize_core() {
- cursor_shape = p_shape;
- if (get_mouse_mode() != MOUSE_MODE_HIDDEN)
- set_css_cursor(godot2dom_cursor(cursor_shape));
+ OS_Unix::initialize_core();
+ FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES);
}
-void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
-}
+Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-void OS_JavaScript::main_loop_begin() {
+ EmscriptenWebGLContextAttributes attributes;
+ emscripten_webgl_init_context_attributes(&attributes);
+ attributes.alpha = false;
+ attributes.antialias = false;
+ ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
+ switch (p_video_driver) {
+ case VIDEO_DRIVER_GLES3:
+ attributes.majorVersion = 2;
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
+ break;
+ case VIDEO_DRIVER_GLES2:
+ attributes.majorVersion = 1;
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ break;
+ }
+
+ video_driver_index = p_video_driver;
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes);
+ ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available");
+ ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE);
+
+ video_mode = p_desired;
+ // Can't fulfil fullscreen request during start-up due to browser security.
+ video_mode.fullscreen = false;
+ /* clang-format off */
+ if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) {
+ /* clang-format on */
+ set_window_size(Size2(video_mode.width, video_mode.height));
+ } else {
+ set_window_size(get_window_size());
+ }
- if (main_loop)
- main_loop->init();
+ char locale_ptr[16];
+ /* clang-format off */
+ EM_ASM_ARGS({
+ stringToUTF8(Module.locale, $0, 16);
+ }, locale_ptr);
+ /* clang-format on */
+ setenv("LANG", locale_ptr, true);
+
+ AudioDriverManager::initialize(p_audio_driver);
+ VisualServer *visual_server = memnew(VisualServerRaster());
+ input = memnew(InputDefault);
+
+ EMSCRIPTEN_RESULT result;
+#define EM_CHECK(ev) \
+ if (result != EMSCRIPTEN_RESULT_SUCCESS) \
+ ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result))
+#define SET_EM_CALLBACK(target, ev, cb) \
+ result = emscripten_set_##ev##_callback(target, NULL, true, &cb); \
+ EM_CHECK(ev)
+#define SET_EM_CALLBACK_NOTARGET(ev, cb) \
+ result = emscripten_set_##ev##_callback(NULL, true, &cb); \
+ EM_CHECK(ev)
+ // These callbacks from Emscripten's html5.h suffice to access most
+ // JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM
+ // is used below.
+ SET_EM_CALLBACK("#window", mousemove, mousemove_callback)
+ SET_EM_CALLBACK("#canvas", mousedown, mouse_button_callback)
+ SET_EM_CALLBACK("#window", mouseup, mouse_button_callback)
+ SET_EM_CALLBACK("#window", wheel, wheel_callback)
+ SET_EM_CALLBACK("#window", touchstart, touch_press_callback)
+ SET_EM_CALLBACK("#window", touchmove, touchmove_callback)
+ SET_EM_CALLBACK("#window", touchend, touch_press_callback)
+ SET_EM_CALLBACK("#window", touchcancel, touch_press_callback)
+ SET_EM_CALLBACK("#canvas", keydown, keydown_callback)
+ SET_EM_CALLBACK("#canvas", keypress, keypress_callback)
+ SET_EM_CALLBACK("#canvas", keyup, keyup_callback)
+ SET_EM_CALLBACK(NULL, resize, browser_resize_callback)
+ SET_EM_CALLBACK(NULL, fullscreenchange, fullscreen_change_callback)
+ SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
+ SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
+#undef SET_EM_CALLBACK_NODATA
+#undef SET_EM_CALLBACK
+#undef EM_CHECK
/* clang-format off */
EM_ASM_ARGS({
const send_notification = cwrap('send_notification', null, ['number']);
- const notifs = arguments;
- (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) {
- Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i]));
+ const notifications = arguments;
+ (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) {
+ Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index]));
});
},
MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
@@ -772,22 +747,44 @@ void OS_JavaScript::main_loop_begin() {
MainLoop::NOTIFICATION_WM_FOCUS_OUT
);
/* clang-format on */
+
+ visual_server->init();
+
+ return OK;
}
-bool OS_JavaScript::main_loop_iterate() {
+void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
- if (!main_loop)
- return false;
+ main_loop = p_main_loop;
+ input->set_main_loop(p_main_loop);
+}
+
+MainLoop *OS_JavaScript::get_main_loop() const {
+
+ return main_loop;
+}
+
+void OS_JavaScript::run_async() {
+
+ main_loop->init();
+ emscripten_set_main_loop(main_loop_callback, -1, false);
+}
+
+void OS_JavaScript::main_loop_callback() {
+
+ get_singleton()->main_loop_iterate();
+}
+
+bool OS_JavaScript::main_loop_iterate() {
- if (idbfs_available && time_to_save_sync >= 0) {
- int64_t newtime = get_ticks_msec();
- int64_t elapsed = newtime - last_sync_time;
- last_sync_time = newtime;
+ if (is_userfs_persistent() && sync_wait_time >= 0) {
+ int64_t current_time = get_ticks_msec();
+ int64_t elapsed_time = current_time - last_sync_check_time;
+ last_sync_check_time = current_time;
- time_to_save_sync -= elapsed;
+ sync_wait_time -= elapsed_time;
- if (time_to_save_sync < 0) {
- //time to sync, for real
+ if (sync_wait_time < 0) {
/* clang-format off */
EM_ASM(
FS.syncfs(function(err) {
@@ -812,121 +809,101 @@ bool OS_JavaScript::main_loop_iterate() {
return Main::iteration();
}
-void OS_JavaScript::main_loop_end() {
+void OS_JavaScript::delete_main_loop() {
- if (main_loop)
- main_loop->finish();
+ memdelete(main_loop);
}
-void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) {
+void OS_JavaScript::finalize() {
- input->set_accelerometer(p_accelerometer);
+ memdelete(input);
}
-bool OS_JavaScript::has_touchscreen_ui_hint() const {
+// Miscellaneous
- /* clang-format off */
- return EM_ASM_INT_V(
- return 'ontouchstart' in window;
- );
- /* clang-format on */
+extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) {
+
+ if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
+ cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
+ }
+ OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification);
}
-void OS_JavaScript::main_loop_request_quit() {
+bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
+
+ if (p_feature == "HTML5" || p_feature == "web")
+ return true;
+
+#ifdef JAVASCRIPT_EVAL_ENABLED
+ if (p_feature == "JavaScript")
+ return true;
+#endif
+
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context();
+ // All extensions are already automatically enabled, this function allows
+ // checking WebGL extension support without inline JavaScript
+ if (p_feature == "s3tc")
+ return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb");
+ if (p_feature == "etc")
+ return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1");
+ if (p_feature == "etc2")
+ return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc");
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
+ return false;
}
-Error OS_JavaScript::shell_open(String p_uri) {
+void OS_JavaScript::alert(const String &p_alert, const String &p_title) {
+
/* clang-format off */
EM_ASM_({
- window.open(UTF8ToString($0), '_blank');
- }, p_uri.utf8().get_data());
+ window.alert(UTF8ToString($0));
+ }, p_alert.utf8().get_data());
/* clang-format on */
- return OK;
}
-String OS_JavaScript::get_resource_dir() const {
+void OS_JavaScript::set_window_title(const String &p_title) {
- return "/"; //javascript has it's own filesystem for resources inside the APK
+ /* clang-format off */
+ EM_ASM_({
+ document.title = UTF8ToString($0);
+ }, p_title.utf8().get_data());
+ /* clang-format on */
}
-String OS_JavaScript::get_user_data_dir() const {
-
- /*
- if (get_user_data_dir_func)
- return get_user_data_dir_func();
- */
- return "/userfs";
-};
-
String OS_JavaScript::get_executable_path() const {
return OS::get_executable_path();
}
-void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) {
+Error OS_JavaScript::shell_open(String p_uri) {
- OS_JavaScript *os = static_cast<OS_JavaScript *>(get_singleton());
- if (os->idbfs_available && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) {
- os->last_sync_time = OS::get_singleton()->get_ticks_msec();
- os->time_to_save_sync = 5000; //five seconds since last save
- }
+ // Open URI in a new tab, browser will deal with it by protocol.
+ /* clang-format off */
+ EM_ASM_({
+ window.open(UTF8ToString($0), '_blank');
+ }, p_uri.utf8().get_data());
+ /* clang-format on */
+ return OK;
}
-void OS_JavaScript::process_joypads() {
-
- int joy_count = emscripten_get_num_gamepads();
- for (int i = 0; i < joy_count; i++) {
- EmscriptenGamepadEvent state;
- emscripten_get_gamepad_status(i, &state);
- if (state.connected) {
+String OS_JavaScript::get_name() {
- int num_buttons = MIN(state.numButtons, 18);
- int num_axes = MIN(state.numAxes, 8);
- for (int j = 0; j < num_buttons; j++) {
+ return "HTML5";
+}
- float value = state.analogButton[j];
- if (String(state.mapping) == "standard" && (j == 6 || j == 7)) {
- InputDefault::JoyAxis jx;
- jx.min = 0;
- jx.value = value;
- input->joy_axis(i, j, jx);
- } else {
- input->joy_button(i, j, value);
- }
- }
- for (int j = 0; j < num_axes; j++) {
+bool OS_JavaScript::can_draw() const {
- InputDefault::JoyAxis jx;
- jx.min = -1;
- jx.value = state.axis[j];
- input->joy_axis(i, j, jx);
- }
- }
- }
+ return true; // Always?
}
-bool OS_JavaScript::joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event) {
- if (p_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) {
+String OS_JavaScript::get_user_data_dir() const {
- String guid = "";
- if (String(p_event->mapping) == "standard")
- guid = "Default HTML5 Gamepad";
- input->joy_connection_changed(p_event->index, true, String(p_event->id), guid);
- } else {
- input->joy_connection_changed(p_event->index, false, "");
- }
- return true;
-}
+ return "/userfs";
+};
-bool OS_JavaScript::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
+String OS_JavaScript::get_resource_dir() const {
-String OS_JavaScript::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
+ return "/";
}
OS::PowerState OS_JavaScript::get_power_state() {
@@ -947,59 +924,53 @@ int OS_JavaScript::get_power_percent_left() {
return -1;
}
-bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
+void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags) {
- if (p_feature == "HTML5" || p_feature == "web")
- return true;
-
-#ifdef JAVASCRIPT_EVAL_ENABLED
- if (p_feature == "JavaScript")
- return true;
-#endif
+ OS_JavaScript *os = get_singleton();
+ if (os->is_userfs_persistent() && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) {
+ os->last_sync_check_time = OS::get_singleton()->get_ticks_msec();
+ // Wait five seconds in case more files are about to be closed.
+ os->sync_wait_time = 5000;
+ }
+}
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context();
- // all extensions are already automatically enabled, this function allows
- // checking WebGL extension support without inline JavaScript
- if (p_feature == "s3tc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb"))
- return true;
- if (p_feature == "etc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1"))
- return true;
- if (p_feature == "etc2" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc"))
- return true;
+void OS_JavaScript::set_idb_available(bool p_idb_available) {
- return false;
+ idb_available = p_idb_available;
}
-void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) {
+bool OS_JavaScript::is_userfs_persistent() const {
- idbfs_available = p_idbfs_available;
+ return idb_available;
}
-bool OS_JavaScript::is_userfs_persistent() const {
+OS_JavaScript *OS_JavaScript::get_singleton() {
- return idbfs_available;
+ return static_cast<OS_JavaScript *>(OS::get_singleton());
}
-OS_JavaScript::OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func) {
+OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
+
+ List<String> arguments;
+ for (int i = 1; i < p_argc; i++) {
+ arguments.push_back(String::utf8(p_argv[i]));
+ }
+ set_cmdline(p_argv[0], arguments);
- set_cmdline(p_execpath, get_cmdline_args());
- main_loop = NULL;
window_maximized = false;
- soft_fs_enabled = false;
+ soft_fullscreen_enabled = false;
canvas_size_adjustment_requested = false;
- get_user_data_dir_func = p_get_user_data_dir_func;
- FileAccessUnix::close_notification_func = _close_notification_funcs;
+ main_loop = NULL;
- idbfs_available = false;
- time_to_save_sync = -1;
+ idb_available = false;
+ sync_wait_time = -1;
+
+ AudioDriverManager::add_driver(&audio_driver_javascript);
Vector<Logger *> loggers;
loggers.push_back(memnew(StdLogger));
_set_logger(memnew(CompositeLogger(loggers)));
- AudioDriverManager::add_driver(&audio_driver_javascript);
-}
-
-OS_JavaScript::~OS_JavaScript() {
+ FileAccessUnix::close_notification_func = file_access_close_callback;
}
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 46eb1b3f13..915320fe39 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -32,51 +32,59 @@
#define OS_JAVASCRIPT_H
#include "audio_driver_javascript.h"
-#include "drivers/unix/os_unix.h"
#include "main/input_default.h"
-#include "os/input.h"
-#include "os/main_loop.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
+#include "unix/os_unix.h"
#include <emscripten/html5.h>
-typedef String (*GetUserDataDirFunc)();
-
class OS_JavaScript : public OS_Unix {
- bool idbfs_available;
- int64_t time_to_save_sync;
- int64_t last_sync_time;
-
- VisualServer *visual_server;
- AudioDriverJavaScript audio_driver_javascript;
-
- InputDefault *input;
+ VideoMode video_mode;
Vector2 windowed_size;
bool window_maximized;
- bool soft_fs_enabled;
+ bool soft_fullscreen_enabled;
bool canvas_size_adjustment_requested;
- VideoMode video_mode;
+
+ InputDefault *input;
+ Ref<InputEventKey> deferred_key_event;
CursorShape cursor_shape;
+ Point2 touches[32];
+
MainLoop *main_loop;
+ AudioDriverJavaScript audio_driver_javascript;
+
+ bool idb_available;
+ int64_t sync_wait_time;
+ int64_t last_sync_check_time;
+
+ static EM_BOOL browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data);
+ static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
+
+ static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+ static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+ static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+
+ static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
+ static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
- GetUserDataDirFunc get_user_data_dir_func;
+ static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
- static void _close_notification_funcs(const String &p_file, int p_flags);
+ static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
+ static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
+ static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data);
void process_joypads();
- void set_css_cursor(const char *);
- const char *get_css_cursor() const;
+ static void main_loop_callback();
-public:
- // functions used by main to initialize/deintialize the OS
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
+ static void file_access_close_callback(const String &p_file, int p_flags);
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_driver_name(int p_driver) const;
+ int video_driver_index;
+
+protected:
+ virtual int get_current_video_driver() const;
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -86,77 +94,64 @@ public:
virtual void finalize();
- typedef int64_t ProcessID;
-
- //static OS* get_singleton();
-
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
-
- virtual void set_mouse_mode(MouseMode p_mode);
- virtual MouseMode get_mouse_mode() const;
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
+ virtual bool _check_internal_feature_support(const String &p_feature);
- //virtual void set_clipboard(const String& p_text);
- //virtual String get_clipboard() const;
+public:
+ // Override return type to make writing static callbacks less tedious.
+ static OS_JavaScript *get_singleton();
virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
virtual VideoMode get_video_mode(int p_screen = 0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
-
virtual void set_window_size(const Size2);
virtual Size2 get_window_size() const;
virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const { return window_maximized; }
- virtual void set_window_fullscreen(bool p_enable);
+ virtual bool is_window_maximized() const;
+ virtual void set_window_fullscreen(bool p_enabled);
virtual bool is_window_fullscreen() const;
+ virtual Size2 get_screen_size(int p_screen = -1) const;
- void request_canvas_size_adjustment();
+ virtual Point2 get_mouse_position() const;
+ virtual int get_mouse_button_state() const;
+ virtual void set_cursor_shape(CursorShape p_shape);
+ virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
+ virtual void set_mouse_mode(MouseMode p_mode);
+ virtual MouseMode get_mouse_mode() const;
- virtual String get_name();
- virtual MainLoop *get_main_loop() const;
+ virtual bool has_touchscreen_ui_hint() const;
- virtual bool can_draw() const;
+ virtual bool is_joy_known(int p_device);
+ virtual String get_joy_guid(int p_device) const;
- virtual bool is_userfs_persistent() const;
+ virtual int get_video_driver_count() const;
+ virtual const char *get_video_driver_name(int p_driver) const;
- virtual void set_cursor_shape(CursorShape p_shape);
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
+ virtual int get_audio_driver_count() const;
+ virtual const char *get_audio_driver_name(int p_driver) const;
- void main_loop_begin();
+ virtual MainLoop *get_main_loop() const;
+ void run_async();
bool main_loop_iterate();
- void main_loop_request_quit();
- void main_loop_end();
- void main_loop_focusout();
- void main_loop_focusin();
- virtual bool has_touchscreen_ui_hint() const;
-
- virtual Error shell_open(String p_uri);
- virtual String get_user_data_dir() const;
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+ virtual void set_window_title(const String &p_title);
String get_executable_path() const;
- virtual String get_resource_dir() const;
-
- void process_accelerometer(const Vector3 &p_accelerometer);
- void push_input(const Ref<InputEvent> &p_ev);
+ virtual Error shell_open(String p_uri);
+ virtual String get_name();
+ virtual bool can_draw() const;
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
- bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event);
+ virtual String get_resource_dir() const;
+ virtual String get_user_data_dir() const;
virtual OS::PowerState get_power_state();
virtual int get_power_seconds_left();
virtual int get_power_percent_left();
- virtual bool _check_internal_feature_support(const String &p_feature);
-
- void set_idbfs_available(bool p_idbfs_available);
+ void set_idb_available(bool p_idb_available);
+ virtual bool is_userfs_persistent() const;
- OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func);
- ~OS_JavaScript();
+ OS_JavaScript(int p_argc, char *p_argv[]);
};
#endif
diff --git a/platform/javascript/run_icon.png b/platform/javascript/run_icon.png
index dedee6f479..574abb0150 100644
--- a/platform/javascript/run_icon.png
+++ b/platform/javascript/run_icon.png
Binary files differ
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 99ce25adfb..1664c5ce8e 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -78,6 +78,10 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 72b8aa99f8..1cc5c619fe 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -39,14 +39,21 @@ def configure(env):
## Build type
if (env["target"] == "release"):
- env.Prepend(CCFLAGS=['-O3', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Prepend(CCFLAGS=['-O3', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os','-ftree-vectorize', '-msse2'])
+
if (env["debug_symbols"] == "yes"):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
env.Prepend(CCFLAGS=['-g2'])
elif (env["target"] == "release_debug"):
- env.Prepend(CCFLAGS=['-O2', '-DDEBUG_ENABLED'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Prepend(CCFLAGS=['-O2', '-DDEBUG_ENABLED'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os', '-DDEBUG_ENABLED'])
if (env["debug_symbols"] == "yes"):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
diff --git a/platform/osx/logo.png b/platform/osx/logo.png
index 93c6890e85..62086fc415 100644
--- a/platform/osx/logo.png
+++ b/platform/osx/logo.png
Binary files differ
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 7bd5b16f36..3d14a6d4fb 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -137,6 +137,9 @@ public:
void _update_window();
+ int video_driver_index;
+ virtual int get_current_video_driver() const;
+
protected:
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 4ece1e0325..282f5e2d1b 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -625,10 +625,18 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
- (void)otherMouseDown:(NSEvent *)event {
- if ((int)[event buttonNumber] != 2)
- return;
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
+
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
- _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
+
+ } else {
+ return;
+ }
}
- (void)otherMouseDragged:(NSEvent *)event {
@@ -637,10 +645,18 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
- (void)otherMouseUp:(NSEvent *)event {
- if ((int)[event buttonNumber] != 2)
- return;
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
- _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
+
+ } else {
+ return;
+ }
}
- (void)mouseExited:(NSEvent *)event {
@@ -1160,6 +1176,10 @@ static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplay
displays_arrangement_dirty = true;
}
+int OS_OSX::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
/*** OSX INITIALIZATION ***/
@@ -1256,6 +1276,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
}
+ video_driver_index = p_video_driver;
+
ADD_ATTR2(NSOpenGLPFAColorSize, colorBits);
/*
diff --git a/platform/server/logo.png b/platform/server/logo.png
index 5e98ac26ec..8666ada9ca 100644
--- a/platform/server/logo.png
+++ b/platform/server/logo.png
Binary files differ
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 3b1be780d4..1c17780ad7 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -28,16 +28,17 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "os_server.h"
+
#include "drivers/dummy/audio_driver_dummy.h"
#include "drivers/dummy/rasterizer_dummy.h"
#include "drivers/dummy/texture_loader_dummy.h"
#include "print_string.h"
#include "servers/visual/visual_server_raster.h"
-#include <stdio.h>
-#include <stdlib.h>
#include "main/main.h"
+#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
int OS_Server::get_video_driver_count() const {
@@ -58,6 +59,10 @@ const char *OS_Server::get_audio_driver_name(int p_driver) const {
return "Dummy";
}
+int OS_Server::get_current_video_driver() const {
+ return video_driver_index;
+}
+
void OS_Server::initialize_core() {
crash_handler.initialize();
@@ -73,6 +78,8 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
RasterizerDummy::make_current();
+ video_driver_index = p_video_driver; // unused in server platform, but should still be initialized
+
visual_server = memnew(VisualServerRaster);
visual_server->init();
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index f1a880ecc2..07d70e5236 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -30,12 +30,12 @@
#ifndef OS_SERVER_H
#define OS_SERVER_H
-#include "../x11/crash_handler_x11.h"
-#include "../x11/power_x11.h"
#include "drivers/dummy/texture_loader_dummy.h"
#include "drivers/rtaudio/audio_driver_rtaudio.h"
#include "drivers/unix/os_unix.h"
#include "main/input_default.h"
+#include "platform/x11/crash_handler_x11.h"
+#include "platform/x11/power_x11.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
@@ -47,7 +47,6 @@
class OS_Server : public OS_Unix {
- //Rasterizer *rasterizer;
VisualServer *visual_server;
VideoMode current_videomode;
List<String> args;
@@ -66,12 +65,14 @@ class OS_Server : public OS_Unix {
CrashHandler crash_handler;
+ int video_driver_index;
+
ResourceFormatDummyTexture *resource_loader_dummy;
protected:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
-
+ virtual int get_current_video_driver() const;
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp
index c18aa36402..b769925849 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -143,14 +143,13 @@ void App::SetWindow(CoreWindow ^ p_window) {
window->KeyUp +=
ref new TypedEventHandler<CoreWindow ^, KeyEventArgs ^>(this, &App::OnKeyUp);
+ os->set_window(window);
+
unsigned int argc;
char **argv = get_command_line(&argc);
Main::setup("uwp", argc, argv, false);
- // The CoreWindow has been created, so EGL can be initialized.
- ContextEGL *context = memnew(ContextEGL(window));
- os->set_gl_context(context);
UpdateWindowSize(Size(window->Bounds.Width, window->Bounds.Height));
Main::setup2();
@@ -513,7 +512,7 @@ char **App::get_command_line(unsigned int *out_argc) {
if (f == NULL) {
- wprintf(L"Couldn't open command line file.");
+ wprintf(L"Couldn't open command line file.\n");
return fail_cl;
}
@@ -527,7 +526,7 @@ char **App::get_command_line(unsigned int *out_argc) {
if (r < 4) {
fclose(f);
- wprintf(L"Wrong cmdline length.");
+ wprintf(L"Wrong cmdline length.\n");
return (fail_cl);
}
@@ -539,7 +538,7 @@ char **App::get_command_line(unsigned int *out_argc) {
if (r < 4) {
fclose(f);
- wprintf(L"Wrong cmdline param length.");
+ wprintf(L"Wrong cmdline param length.\n");
return (fail_cl);
}
@@ -547,7 +546,7 @@ char **App::get_command_line(unsigned int *out_argc) {
if (strlen > CMD_MAX_LEN) {
fclose(f);
- wprintf(L"Wrong command length.");
+ wprintf(L"Wrong command length.\n");
return (fail_cl);
}
@@ -568,7 +567,7 @@ char **App::get_command_line(unsigned int *out_argc) {
delete[] arg;
fclose(f);
- wprintf(L"Error reading command.");
+ wprintf(L"Error reading command.\n");
return (fail_cl);
}
}
diff --git a/platform/uwp/gl_context_egl.cpp b/platform/uwp/gl_context_egl.cpp
index 88c9c8d687..6c60b27f5a 100644
--- a/platform/uwp/gl_context_egl.cpp
+++ b/platform/uwp/gl_context_egl.cpp
@@ -93,12 +93,26 @@ Error ContextEGL::initialize() {
EGLint numConfigs = 0;
EGLint majorVersion = 1;
- EGLint minorVersion = 0;
+ EGLint minorVersion;
+ if (driver == GLES_2_0) {
+ minorVersion = 0;
+ } else {
+ minorVersion = 5;
+ }
EGLDisplay display = EGL_NO_DISPLAY;
EGLContext context = EGL_NO_CONTEXT;
EGLSurface surface = EGL_NO_SURFACE;
EGLConfig config = nullptr;
- EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, EGL_NONE };
+ EGLint contextAttribs[3];
+ if (driver == GLES_2_0) {
+ contextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+ contextAttribs[1] = 2;
+ contextAttribs[2] = EGL_NONE;
+ } else {
+ contextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+ contextAttribs[1] = 3;
+ contextAttribs[2] = EGL_NONE;
+ }
try {
@@ -114,7 +128,8 @@ Error ContextEGL::initialize() {
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices.
// Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it.
- //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE,
+ EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER,
+ EGL_TRUE,
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call
// the IDXGIDevice3::Trim method on behalf of the application when it gets suspended.
@@ -193,13 +208,12 @@ void ContextEGL::cleanup() {
}
};
-ContextEGL::ContextEGL(CoreWindow ^ p_window) :
+ContextEGL::ContextEGL(CoreWindow ^ p_window, Driver p_driver) :
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
- mEglSurface(EGL_NO_SURFACE) {
-
- window = p_window;
-};
+ mEglSurface(EGL_NO_SURFACE),
+ driver(p_driver),
+ window(p_window) {}
ContextEGL::~ContextEGL() {
diff --git a/platform/uwp/gl_context_egl.h b/platform/uwp/gl_context_egl.h
index 527baf1054..df0108c124 100644
--- a/platform/uwp/gl_context_egl.h
+++ b/platform/uwp/gl_context_egl.h
@@ -42,6 +42,13 @@ using namespace Windows::UI::Core;
class ContextEGL : public ContextGL {
+public:
+ enum Driver {
+ GLES_2_0,
+ GLES_3_0,
+ };
+
+private:
CoreWindow ^ window;
EGLDisplay mEglDisplay;
@@ -53,6 +60,8 @@ class ContextEGL : public ContextGL {
bool vsync;
+ Driver driver;
+
public:
virtual void release_current();
@@ -70,7 +79,7 @@ public:
void cleanup();
- ContextEGL(CoreWindow ^ p_window);
+ ContextEGL(CoreWindow ^ p_window, Driver p_driver);
~ContextEGL();
};
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index d00da3dbcd..8549a44ce5 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -30,6 +30,7 @@
#include "os_uwp.h"
+#include "drivers/gles2/rasterizer_gles2.h"
#include "drivers/gles3/rasterizer_gles3.h"
#include "drivers/unix/ip_unix.h"
#include "drivers/windows/dir_access_windows.h"
@@ -66,12 +67,7 @@ using namespace Windows::ApplicationModel::DataTransfer;
using namespace concurrency;
int OSUWP::get_video_driver_count() const {
-
- return 1;
-}
-const char *OSUWP::get_video_driver_name(int p_driver) const {
-
- return "GLES3";
+ return 2;
}
Size2 OSUWP::get_window_size() const {
@@ -81,6 +77,10 @@ Size2 OSUWP::get_window_size() const {
return size;
}
+int OSUWP::get_current_video_driver() const {
+ return video_driver_index;
+}
+
void OSUWP::set_window_size(const Size2 p_size) {
Windows::Foundation::Size new_size;
@@ -133,18 +133,6 @@ void OSUWP::set_keep_screen_on(bool p_enabled) {
OS::set_keep_screen_on(p_enabled);
}
-int OSUWP::get_audio_driver_count() const {
-
- return AudioDriverManager::get_driver_count();
-}
-
-const char *OSUWP::get_audio_driver_name(int p_driver) const {
-
- AudioDriver *driver = AudioDriverManager::get_driver(p_driver);
- ERR_FAIL_COND_V(!driver, "");
- return AudioDriverManager::get_driver(p_driver)->get_name();
-}
-
void OSUWP::initialize_core() {
last_button_state = 0;
@@ -185,10 +173,9 @@ bool OSUWP::can_draw() const {
return !minimized;
};
-void OSUWP::set_gl_context(ContextEGL *p_context) {
-
- gl_context = p_context;
-};
+void OSUWP::set_window(Windows::UI::Core::CoreWindow ^ p_window) {
+ window = p_window;
+}
void OSUWP::screen_size_changed() {
@@ -200,6 +187,11 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
main_loop = NULL;
outside = true;
+ if (p_video_driver == VIDEO_DRIVER_GLES2) {
+ gl_context = memnew(ContextEGL(window, ContextEGL::GLES_2_0));
+ } else {
+ gl_context = memnew(ContextEGL(window, ContextEGL::GLES_3_0));
+ }
gl_context->initialize();
VideoMode vm;
vm.width = gl_context->get_window_width();
@@ -240,10 +232,17 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
gl_context->make_current();
- RasterizerGLES3::register_config();
- RasterizerGLES3::make_current();
+ if (p_video_driver == VIDEO_DRIVER_GLES2) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
+ }
gl_context->set_use_vsync(vm.use_vsync);
+ video_driver_index = p_video_driver;
+
visual_server = memnew(VisualServerRaster);
// FIXME: Reimplement threaded rendering? Or remove?
/*
@@ -297,7 +296,7 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
if (is_keep_screen_on())
display_request->RequestActive();
- set_keep_screen_on(GLOBAL_DEF("display/window/keep_screen_on", true));
+ set_keep_screen_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
return OK;
}
@@ -392,7 +391,6 @@ void OSUWP::ManagedType::update_clipboard() {
if (data->Contains(StandardDataFormats::Text)) {
create_task(data->GetTextAsync()).then([this](Platform::String ^ clipboard_content) {
-
this->clipboard = clipboard_content;
});
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 95afdab469..3b48063fe9 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -96,8 +96,10 @@ private:
int pressrc;
ContextEGL *gl_context;
+ Windows::UI::Core::CoreWindow ^ window;
VideoMode video_mode;
+ int video_driver_index;
MainLoop *main_loop;
@@ -153,10 +155,7 @@ private:
// functions used by main to initialize/deintialize the OS
protected:
virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
-
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_driver_name(int p_driver) const;
+ virtual int get_current_video_driver() const;
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -231,7 +230,7 @@ public:
virtual bool _check_internal_feature_support(const String &p_feature);
- void set_gl_context(ContextEGL *p_context);
+ void set_window(Windows::UI::Core::CoreWindow ^ p_window);
void screen_size_changed();
virtual void release_rendering_thread();
diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp
index 804c2d44eb..76a227c608 100644
--- a/platform/windows/crash_handler_win.cpp
+++ b/platform/windows/crash_handler_win.cpp
@@ -124,6 +124,9 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
// Load the symbols:
if (!SymInitialize(process, NULL, false))
return EXCEPTION_CONTINUE_SEARCH;
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 05806d2fe8..cacb573236 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -166,12 +166,18 @@ def configure_msvc(env, manual_msvc_config):
# Build type
if (env["target"] == "release"):
- env.Append(CCFLAGS=['/O2'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Append(CCFLAGS=['/O2'])
+ else: # optimize for size
+ env.Append(CCFLAGS=['/O1'])
env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS'])
env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup'])
elif (env["target"] == "release_debug"):
- env.Append(CCFLAGS=['/O2'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Append(CCFLAGS=['/O2'])
+ else: # optimize for size
+ env.Append(CCFLAGS=['/O1'])
env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED'])
env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
@@ -247,10 +253,14 @@ def configure_mingw(env):
if (env["target"] == "release"):
env.Append(CCFLAGS=['-msse2'])
- if (env["bits"] == "64"):
- env.Append(CCFLAGS=['-O3'])
- else:
- env.Append(CCFLAGS=['-O2'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ if (env["bits"] == "64"):
+ env.Append(CCFLAGS=['-O3'])
+ else:
+ env.Append(CCFLAGS=['-O2'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os'])
+
env.Append(LINKFLAGS=['-Wl,--subsystem,windows'])
@@ -265,7 +275,11 @@ def configure_mingw(env):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
env.Prepend(CCFLAGS=['-g2'])
-
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Append(CCFLAGS=['-O2'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os'])
+
elif (env["target"] == "debug"):
env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED'])
diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp
index 80f53dd1a1..504a9a0380 100644
--- a/platform/windows/godot_win.cpp
+++ b/platform/windows/godot_win.cpp
@@ -176,8 +176,8 @@ int _main() {
}
int main(int _argc, char **_argv) {
-// _argc and _argv are ignored
-// we are going to use the WideChar version of them instead
+ // _argc and _argv are ignored
+ // we are going to use the WideChar version of them instead
#ifdef CRASH_HANDLER_EXCEPTION
__try {
diff --git a/platform/windows/logo.png b/platform/windows/logo.png
index 4376abd563..f06b463850 100644
--- a/platform/windows/logo.png
+++ b/platform/windows/logo.png
Binary files differ
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index ca6c793d5d..ac37e1246d 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -70,6 +70,30 @@ __attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001;
#define WM_TOUCH 576
#endif
+typedef struct {
+ int count;
+ int screen;
+ Size2 size;
+} EnumSizeData;
+
+typedef struct {
+ int count;
+ int screen;
+ Point2 pos;
+} EnumPosData;
+
+static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ EnumSizeData *data = (EnumSizeData *)dwData;
+ if (data->count == data->screen) {
+ data->size.x = lprcMonitor->right - lprcMonitor->left;
+ data->size.y = lprcMonitor->bottom - lprcMonitor->top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = NULL;
@@ -410,11 +434,12 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0;
bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0;
bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0;
+ bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0;
+ bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0;
mm->set_button_mask(bmask);
last_button_state = mm->get_button_mask();
- /*mm->get_button_mask()|=(wParam&MK_XBUTTON1)?(1<<5):0;
- mm->get_button_mask()|=(wParam&MK_XBUTTON2)?(1<<6):0;*/
+
mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
@@ -455,6 +480,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
} break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
+ if (input->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translations for left button
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsPenEvent(extra)) {
+ break;
+ }
+ }
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
@@ -464,161 +496,168 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
- /*case WM_XBUTTONDOWN:
- case WM_XBUTTONUP: */ {
-
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translation
- LPARAM extra = GetMessageExtraInfo();
- if (IsPenEvent(extra)) {
- break;
- }
- }
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP: {
- Ref<InputEventMouseButton> mb;
- mb.instance();
-
- switch (uMsg) {
- case WM_LBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(1);
- } break;
- case WM_LBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(1);
- } break;
- case WM_MBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(3);
-
- } break;
- case WM_MBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(3);
- } break;
- case WM_RBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(2);
- } break;
- case WM_RBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(2);
- } break;
- case WM_LBUTTONDBLCLK: {
-
- mb->set_pressed(true);
- mb->set_button_index(1);
- mb->set_doubleclick(true);
- } break;
- case WM_RBUTTONDBLCLK: {
-
- mb->set_pressed(true);
- mb->set_button_index(2);
- mb->set_doubleclick(true);
- } break;
- case WM_MBUTTONDBLCLK: {
-
- mb->set_pressed(true);
- mb->set_button_index(3);
- mb->set_doubleclick(true);
- } break;
- case WM_MOUSEWHEEL: {
-
- mb->set_pressed(true);
- int motion = (short)HIWORD(wParam);
- if (!motion)
- return 0;
-
- if (motion > 0)
- mb->set_button_index(BUTTON_WHEEL_UP);
- else
- mb->set_button_index(BUTTON_WHEEL_DOWN);
-
- } break;
- case WM_MOUSEHWHEEL: {
-
- mb->set_pressed(true);
- int motion = (short)HIWORD(wParam);
- if (!motion)
- return 0;
-
- if (motion < 0) {
- mb->set_button_index(BUTTON_WHEEL_LEFT);
- mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
- } else {
- mb->set_button_index(BUTTON_WHEEL_RIGHT);
- mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
- }
- } break;
- /*
- case WM_XBUTTONDOWN: {
- mb->is_pressed()=true;
- mb->get_button_index()=(HIWORD(wParam)==XBUTTON1)?6:7;
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+
+ switch (uMsg) {
+ case WM_LBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(1);
} break;
- case WM_XBUTTONUP:
- mb->is_pressed()=true;
- mb->get_button_index()=(HIWORD(wParam)==XBUTTON1)?6:7;
- } break;*/
- default: { return 0; }
- }
+ case WM_LBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(1);
+ } break;
+ case WM_MBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(3);
- mb->set_control((wParam & MK_CONTROL) != 0);
- mb->set_shift((wParam & MK_SHIFT) != 0);
- mb->set_alt(alt_mem);
- //mb->get_alt()=(wParam&MK_MENU)!=0;
- int bmask = 0;
- bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0;
- bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0;
- bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0;
- mb->set_button_mask(bmask);
-
- last_button_state = mb->get_button_mask();
- /*
- mb->get_button_mask()|=(wParam&MK_XBUTTON1)?(1<<5):0;
- mb->get_button_mask()|=(wParam&MK_XBUTTON2)?(1<<6):0;*/
- mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- mb->set_position(Vector2(old_x, old_y));
- }
+ } break;
+ case WM_MBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(3);
+ } break;
+ case WM_RBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(2);
+ } break;
+ case WM_RBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(2);
+ } break;
+ case WM_LBUTTONDBLCLK: {
- if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
- if (mb->is_pressed()) {
+ mb->set_pressed(true);
+ mb->set_button_index(1);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_RBUTTONDBLCLK: {
- if (++pressrc > 0)
- SetCapture(hWnd);
- } else {
+ mb->set_pressed(true);
+ mb->set_button_index(2);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_MBUTTONDBLCLK: {
+
+ mb->set_pressed(true);
+ mb->set_button_index(3);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_MOUSEWHEEL: {
- if (--pressrc <= 0) {
- ReleaseCapture();
- pressrc = 0;
- }
+ mb->set_pressed(true);
+ int motion = (short)HIWORD(wParam);
+ if (!motion)
+ return 0;
+
+ if (motion > 0)
+ mb->set_button_index(BUTTON_WHEEL_UP);
+ else
+ mb->set_button_index(BUTTON_WHEEL_DOWN);
+
+ } break;
+ case WM_MOUSEHWHEEL: {
+
+ mb->set_pressed(true);
+ int motion = (short)HIWORD(wParam);
+ if (!motion)
+ return 0;
+
+ if (motion < 0) {
+ mb->set_button_index(BUTTON_WHEEL_LEFT);
+ mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
+ } else {
+ mb->set_button_index(BUTTON_WHEEL_RIGHT);
+ mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
}
- } else if (mouse_mode != MOUSE_MODE_CAPTURED) {
- // for reasons unknown to mankind, wheel comes in screen cordinates
- POINT coords;
- coords.x = mb->get_position().x;
- coords.y = mb->get_position().y;
+ } break;
+ case WM_XBUTTONDOWN: {
- ScreenToClient(hWnd, &coords);
+ mb->set_pressed(true);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ } break;
+ case WM_XBUTTONUP: {
- mb->set_position(Vector2(coords.x, coords.y));
- }
+ mb->set_pressed(false);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ } break;
+ case WM_XBUTTONDBLCLK: {
- mb->set_global_position(mb->get_position());
+ mb->set_pressed(true);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ mb->set_doubleclick(true);
+ } break;
+ default: { return 0; }
+ }
+
+ mb->set_control((wParam & MK_CONTROL) != 0);
+ mb->set_shift((wParam & MK_SHIFT) != 0);
+ mb->set_alt(alt_mem);
+ //mb->get_alt()=(wParam&MK_MENU)!=0;
+ int bmask = 0;
+ bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0;
+ bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0;
+ bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0;
+ bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0;
+ bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0;
+ mb->set_button_mask(bmask);
+
+ last_button_state = mb->get_button_mask();
+ mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+
+ mb->set_position(Vector2(old_x, old_y));
+ }
+
+ if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
+ if (mb->is_pressed()) {
+
+ if (++pressrc > 0)
+ SetCapture(hWnd);
+ } else {
- if (main_loop) {
- input->parse_input_event(mb);
- if (mb->is_pressed() && mb->get_button_index() > 3) {
- //send release for mouse wheel
- Ref<InputEventMouseButton> mbd = mb->duplicate();
- mbd->set_pressed(false);
- input->parse_input_event(mbd);
+ if (--pressrc <= 0) {
+ ReleaseCapture();
+ pressrc = 0;
}
}
+ } else if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ // for reasons unknown to mankind, wheel comes in screen cordinates
+ POINT coords;
+ coords.x = mb->get_position().x;
+ coords.y = mb->get_position().y;
+
+ ScreenToClient(hWnd, &coords);
+
+ mb->set_position(Vector2(coords.x, coords.y));
}
- break;
+
+ mb->set_global_position(mb->get_position());
+
+ if (main_loop) {
+ input->parse_input_event(mb);
+ if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) {
+ //send release for mouse wheel
+ Ref<InputEventMouseButton> mbd = mb->duplicate();
+ mbd->set_pressed(false);
+ input->parse_input_event(mbd);
+ }
+ }
+ } break;
case WM_SIZE: {
int window_w = LOWORD(lParam);
@@ -742,13 +781,18 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
for (UINT i = 0; i < cInputs; i++) {
TOUCHINPUT ti = pInputs[i];
+ POINT touch_pos = {
+ TOUCH_COORD_TO_PIXEL(ti.x),
+ TOUCH_COORD_TO_PIXEL(ti.y),
+ };
+ ScreenToClient(hWnd, &touch_pos);
//do something with each touch input entry
if (ti.dwFlags & TOUCHEVENTF_MOVE) {
- _drag_event(ti.x / 100.0f, ti.y / 100.0f, ti.dwID);
+ _drag_event(touch_pos.x, touch_pos.y, ti.dwID);
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
- _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, ti.x / 100.0f, ti.y / 100.0f, ti.dwID);
+ _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
};
}
bHandled = TRUE;
@@ -968,6 +1012,10 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
} SHC_PROCESS_DPI_AWARENESS;
+int OS_Windows::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
main_loop = NULL;
@@ -976,6 +1024,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
WNDCLASSEXW wc;
if (is_hidpi_allowed()) {
+ print_line("hidpi aware?");
HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
if (Shcore != NULL) {
@@ -1020,6 +1069,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
pre_fs_valid = true;
if (video_mode.fullscreen) {
+ /* this returns DPI unaware size, commenting
DEVMODE current;
memset(&current, 0, sizeof(current));
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &current);
@@ -1027,6 +1077,16 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
WindowRect.right = current.dmPelsWidth;
WindowRect.bottom = current.dmPelsHeight;
+ */
+
+ EnumSizeData data = { 0, 0, Size2() };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
+
+ WindowRect.right = data.size.width;
+ WindowRect.bottom = data.size.height;
+
+ print_line("wr right " + itos(WindowRect.right) + ", " + itos(WindowRect.bottom));
+
/* DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
@@ -1125,6 +1185,8 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
RasterizerGLES3::make_current();
}
+ video_driver_index = p_video_driver; // FIXME TODO - FIX IF DRIVER DETECTION HAPPENS AND GLES2 MUST BE USED
+
gl_context->set_use_vsync(video_mode.use_vsync);
#endif
@@ -1451,12 +1513,6 @@ void OS_Windows::set_current_screen(int p_screen) {
set_window_position(ofs + get_screen_position(p_screen));
}
-typedef struct {
- int count;
- int screen;
- Point2 pos;
-} EnumPosData;
-
static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumPosData *data = (EnumPosData *)dwData;
@@ -1476,24 +1532,6 @@ Point2 OS_Windows::get_screen_position(int p_screen) const {
return data.pos;
}
-typedef struct {
- int count;
- int screen;
- Size2 size;
-} EnumSizeData;
-
-static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumSizeData *data = (EnumSizeData *)dwData;
- if (data->count == data->screen) {
- data->size.x = lprcMonitor->right - lprcMonitor->left;
- data->size.y = lprcMonitor->bottom - lprcMonitor->top;
- }
-
- data->count++;
- return TRUE;
-}
-
Size2 OS_Windows::get_screen_size(int p_screen) const {
EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() };
@@ -1553,16 +1591,16 @@ Size2 OS_Windows::get_real_window_size() const {
}
void OS_Windows::set_window_size(const Size2 p_size) {
- video_mode.width = p_size.width;
- video_mode.height = p_size.height;
+ int w = p_size.width;
+ int h = p_size.height;
+
+ video_mode.width = w;
+ video_mode.height = h;
if (video_mode.fullscreen) {
return;
}
- int w = p_size.width;
- int h = p_size.height;
-
RECT rect;
GetWindowRect(hWnd, &rect);
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 19af63bae0..b99d3e3422 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -134,6 +134,7 @@ class OS_Windows : public OS {
PowerWindows *power_manager;
+ int video_driver_index;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
#endif
@@ -153,6 +154,8 @@ class OS_Windows : public OS {
// functions used by main to initialize/deintialize the OS
protected:
+ virtual int get_current_video_driver() const;
+
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp
index d39fc33f81..960105271b 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/x11/crash_handler_x11.cpp
@@ -55,6 +55,10 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 09e16ad078..feaa2e598f 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -62,6 +62,7 @@ def get_opts():
EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')),
BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False),
BoolVariable('touch', 'Enable touch events', True),
+ BoolVariable('execinfo', 'Use libexecinfo on systems where glibc is not available', False),
]
@@ -81,14 +82,22 @@ def configure(env):
if (env["target"] == "release"):
# -O3 -ffast-math is identical to -Ofast. We need to split it out so we can selectively disable
# -ffast-math in code for which it generates wrong results.
- env.Prepend(CCFLAGS=['-O3', '-ffast-math'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Prepend(CCFLAGS=['-O3', '-ffast-math'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os'])
+
if (env["debug_symbols"] == "yes"):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
env.Prepend(CCFLAGS=['-g2'])
elif (env["target"] == "release_debug"):
- env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED'])
+ if (env["optimize"] == "speed"): #optimize for speed (default)
+ env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED'])
+ else: #optimize for size
+ env.Prepend(CCFLAGS=['-Os', '-DDEBUG_ENABLED'])
+
if (env["debug_symbols"] == "yes"):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
@@ -276,6 +285,9 @@ def configure(env):
env.Append(LIBS=['dl'])
if (platform.system().find("BSD") >= 0):
+ env["execinfo"] = True
+
+ if env["execinfo"]:
env.Append(LIBS=['execinfo'])
## Cross-compilation
diff --git a/platform/x11/logo.png b/platform/x11/logo.png
index 1cc93b46ac..078654b757 100644
--- a/platform/x11/logo.png
+++ b/platform/x11/logo.png
Binary files differ
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index d7f042b4fe..af0e02173f 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -84,6 +84,10 @@ void OS_X11::initialize_core() {
OS_Unix::initialize_core();
}
+int OS_X11::get_current_video_driver() const {
+ return video_driver_index;
+}
+
Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
long im_event_mask = 0;
@@ -285,6 +289,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
} break;
}
+ video_driver_index = p_video_driver; // FIXME TODO - FIX IF DRIVER DETECTION HAPPENS AND GLES2 MUST BE USED
+
context_gl->set_use_vsync(current_videomode.use_vsync);
#endif
@@ -1954,6 +1960,7 @@ void OS_X11::process_xevents() {
// to be able to send relative motion events.
Point2i pos(event.xmotion.x, event.xmotion.y);
+#ifdef TOUCH_ENABLED
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false;
// Adding some tolerance to match better Point2i to Vector2
@@ -1965,6 +1972,7 @@ void OS_X11::process_xevents() {
if (filter) {
break;
}
+#endif
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2527,17 +2535,23 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
void OS_X11::release_rendering_thread() {
+#if defined(OPENGL_ENABLED)
context_gl->release_current();
+#endif
}
void OS_X11::make_rendering_thread() {
+#if defined(OPENGL_ENABLED)
context_gl->make_current();
+#endif
}
void OS_X11::swap_buffers() {
+#if defined(OPENGL_ENABLED)
context_gl->swap_buffers();
+#endif
}
void OS_X11::alert(const String &p_alert, const String &p_title) {
@@ -2631,8 +2645,10 @@ String OS_X11::get_joy_guid(int p_device) const {
}
void OS_X11::_set_use_vsync(bool p_enable) {
+#if defined(OPENGL_ENABLED)
if (context_gl)
- return context_gl->set_use_vsync(p_enable);
+ context_gl->set_use_vsync(p_enable);
+#endif
}
/*
bool OS_X11::is_vsync_enabled() const {
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index 8cab23fe63..b67bc824be 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -180,6 +180,7 @@ class OS_X11 : public OS_Unix {
CrashHandler crash_handler;
+ int video_driver_index;
int audio_driver_index;
unsigned int capture_idle;
bool maximized;
@@ -195,6 +196,8 @@ class OS_X11 : public OS_Unix {
Bool xrandr_ext_ok;
protected:
+ virtual int get_current_video_driver() const;
+
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
virtual void finalize();
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 54194ff543..b56eedabc7 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -59,15 +59,36 @@ bool AnimatedSprite::_edit_use_pivot() const {
}
Rect2 AnimatedSprite::_edit_get_rect() const {
+ return _get_rect();
+}
+
+bool AnimatedSprite::_edit_use_rect() const {
if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
- return Node2D::_edit_get_rect();
+ return false;
+ }
+ Ref<Texture> t;
+ if (animation)
+ t = frames->get_frame(animation, frame);
+ if (t.is_null())
+ return false;
+
+ return true;
+}
+
+Rect2 AnimatedSprite::get_anchorable_rect() const {
+ return _get_rect();
+}
+
+Rect2 AnimatedSprite::_get_rect() const {
+ if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
+ return Rect2();
}
Ref<Texture> t;
if (animation)
t = frames->get_frame(animation, frame);
if (t.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = t->get_size();
Point2 ofs = offset;
@@ -80,10 +101,6 @@ Rect2 AnimatedSprite::_edit_get_rect() const {
return Rect2(ofs, s);
}
-bool AnimatedSprite::_edit_use_rect() const {
- return true;
-}
-
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
@@ -175,6 +192,16 @@ void SpriteFrames::get_animation_list(List<StringName> *r_animations) const {
}
}
+Vector<String> SpriteFrames::get_animation_names() const {
+
+ Vector<String> names;
+ for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ names.push_back(E->key());
+ }
+ names.sort();
+ return names;
+}
+
void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) {
ERR_FAIL_COND(p_fps < 0);
@@ -266,6 +293,8 @@ void SpriteFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation);
ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation);
+ ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names);
+
ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed);
ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed);
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index be5b1ef6d6..f6586aff36 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -72,6 +72,7 @@ public:
void rename_animation(const StringName &p_prev, const StringName &p_next);
void get_animation_list(List<StringName> *r_animations) const;
+ Vector<String> get_animation_names() const;
void set_animation_speed(const StringName &p_anim, float p_fps);
float get_animation_speed(const StringName &p_anim) const;
@@ -145,6 +146,7 @@ class AnimatedSprite : public Node2D {
void _reset_timeout();
void _set_playing(bool p_playing);
bool _is_playing() const;
+ Rect2 _get_rect() const;
protected:
static void _bind_methods();
@@ -161,6 +163,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 54541293fd..507499a324 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -36,11 +36,8 @@
void AudioStreamPlayer2D::_mix_audio() {
- if (!stream_playback.is_valid()) {
- return;
- }
-
- if (!active) {
+ if (!stream_playback.is_valid() || !active ||
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -53,7 +50,11 @@ void AudioStreamPlayer2D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- //mix
+ if (stream_paused_fade_out) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
stream_playback->mix(buffer, pitch_scale, buffer_size);
//write all outputs
@@ -83,8 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() {
}
//mix!
- AudioFrame vol_inc = (current.vol - prev_outputs[i].vol) / float(buffer_size);
- AudioFrame vol = current.vol;
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
+ AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
int cc = AudioServer::get_singleton()->get_channel_count();
@@ -125,6 +128,8 @@ void AudioStreamPlayer2D::_mix_audio() {
}
output_ready = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -142,6 +147,17 @@ void AudioStreamPlayer2D::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
+
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
@@ -242,7 +258,6 @@ void AudioStreamPlayer2D::_notification(int p_what) {
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
- ERR_FAIL_COND(!p_stream.is_valid());
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
@@ -254,14 +269,15 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
setseek = -1;
}
- stream = p_stream;
- stream_playback = p_stream->instance_playback();
+ if (p_stream.is_valid()) {
+ stream = p_stream;
+ stream_playback = p_stream->instance_playback();
+ }
AudioServer::get_singleton()->unlock();
- if (stream_playback.is_null()) {
+ if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -288,6 +304,11 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
void AudioStreamPlayer2D::play(float p_from_pos) {
+ if (!is_playing()) {
+ // Reset the prev_output_count if the stream is stopped
+ prev_output_count = 0;
+ }
+
if (stream_playback.is_valid()) {
setplay = p_from_pos;
output_ready = false;
@@ -418,6 +439,20 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
return area_mask;
}
+void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade_in = p_pause ? false : true;
+ stream_paused_fade_out = p_pause ? true : false;
+ }
+}
+
+bool AudioStreamPlayer2D::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
@@ -454,6 +489,9 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_area_mask", "mask"), &AudioStreamPlayer2D::set_area_mask);
ClassDB::bind_method(D_METHOD("get_area_mask"), &AudioStreamPlayer2D::get_area_mask);
+ ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
+
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -461,6 +499,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "1,4096,1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -483,6 +522,9 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
setplay = -1;
output_ready = false;
area_mask = 1;
+ stream_paused = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 9ae8e3a518..e68e6eeca5 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -72,6 +72,9 @@ private:
float volume_db;
float pitch_scale;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -123,6 +126,9 @@ public:
void set_area_mask(uint32_t p_mask);
uint32_t get_area_mask() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer2D();
~AudioStreamPlayer2D();
};
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index caa1adebdb..e06c30ec6b 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -59,6 +59,11 @@ bool BackBufferCopy::_edit_use_rect() const {
return true;
}
+Rect2 BackBufferCopy::get_anchorable_rect() const {
+
+ return rect;
+}
+
void BackBufferCopy::set_rect(const Rect2 &p_rect) {
rect = p_rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 752d56de2b..b1ee12544b 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -58,6 +58,7 @@ public:
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
+ Rect2 get_anchorable_rect() const;
void set_copy_mode(CopyMode p_mode);
CopyMode get_copy_mode() const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index f1c09594da..47326b9be2 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -321,11 +321,6 @@ void CanvasItem::hide() {
_change_notify("visible");
}
-Size2 CanvasItem::_edit_get_minimum_size() const {
-
- return Size2(-1, -1); //no limit
-}
-
void CanvasItem::_update_callback() {
if (!is_inside_tree()) {
@@ -994,7 +989,6 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect);
ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect);
ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect);
- ClassDB::bind_method(D_METHOD("_edit_get_item_and_children_rect"), &CanvasItem::_edit_get_item_and_children_rect);
ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation);
ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation);
ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation);
@@ -1175,21 +1169,6 @@ int CanvasItem::get_canvas_layer() const {
return 0;
}
-Rect2 CanvasItem::_edit_get_item_and_children_rect() const {
-
- Rect2 rect = _edit_get_rect();
-
- for (int i = 0; i < get_child_count(); i++) {
- CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
- if (c) {
- Rect2 sir = c->get_transform().xform(c->_edit_get_item_and_children_rect());
- rect = rect.merge(sir);
- }
- }
-
- return rect;
-}
-
CanvasItem::CanvasItem() :
xform_change(this) {
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 10d5082dfc..1e6a251c9c 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -222,6 +222,9 @@ public:
/* EDITOR */
+ // Select the node
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state){};
virtual Dictionary _edit_get_state() const { return Dictionary(); };
@@ -234,36 +237,21 @@ public:
virtual void _edit_set_scale(const Size2 &p_scale) = 0;
virtual Size2 _edit_get_scale() const = 0;
+ // Used to rotate the node
+ virtual bool _edit_use_rotation() const { return false; };
+ virtual void _edit_set_rotation(float p_rotation){};
+ virtual float _edit_get_rotation() const { return 0.0; };
+
// Used to resize/move the node
+ virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode()
virtual void _edit_set_rect(const Rect2 &p_rect){};
virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
- virtual bool _edit_use_rect() const { return false; };
-
- Rect2 _edit_get_item_and_children_rect() const;
-
- // used to select the node
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-
- // Used to rotate the node
- virtual void
- _edit_set_rotation(float p_rotation){};
- virtual float _edit_get_rotation() const {
- return 0.0;
- };
- virtual bool _edit_use_rotation() const {
- return false;
- };
+ virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD
// Used to set a pivot
+ virtual bool _edit_use_pivot() const { return false; };
virtual void _edit_set_pivot(const Point2 &p_pivot){};
- virtual Point2 _edit_get_pivot() const {
- return Point2();
- };
- virtual bool _edit_use_pivot() const {
- return false;
- };
-
- virtual Size2 _edit_get_minimum_size() const;
+ virtual Point2 _edit_get_pivot() const { return Point2(); };
/* VISIBILITY */
@@ -358,6 +346,9 @@ public:
void set_notify_transform(bool p_enable);
bool is_transform_notification_enabled() const;
+ // Used by control nodes to retreive the parent's anchorable area
+ virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); };
+
int get_canvas_layer() const;
CanvasItem();
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index d05c818ae1..cabd7fddc2 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -38,10 +38,14 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
+ Transform2D global_transform = get_global_transform();
+
if (area)
- Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
+ Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
else
- Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
+ Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
+
+ last_transform = global_transform;
RID space = get_world_2d()->get_space();
if (area) {
@@ -60,10 +64,18 @@ void CollisionObject2D::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
+ Transform2D global_transform = get_global_transform();
+
+ if (only_update_transform_changes && global_transform == last_transform) {
+ return;
+ }
+
if (area)
- Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
+ Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
else
- Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
+ Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
+
+ last_transform = global_transform;
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -318,6 +330,10 @@ void CollisionObject2D::_mouse_exit() {
emit_signal(SceneStringNames::get_singleton()->mouse_exited);
}
+void CollisionObject2D::set_only_update_transform_changes(bool p_enable) {
+ only_update_transform_changes = p_enable;
+}
+
void CollisionObject2D::_update_pickable() {
if (!is_inside_tree())
return;
@@ -384,6 +400,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
pickable = true;
set_notify_transform(true);
total_subshapes = 0;
+ only_update_transform_changes = false;
if (p_area) {
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 6da63d1a0b..29a00bd9f9 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -65,6 +65,8 @@ class CollisionObject2D : public Node2D {
int total_subshapes;
Map<uint32_t, ShapeData> shapes;
+ Transform2D last_transform;
+ bool only_update_transform_changes; //this is used for sync physics in KinematicBody
protected:
CollisionObject2D(RID p_rid, bool p_area);
@@ -78,6 +80,8 @@ protected:
void _mouse_enter();
void _mouse_exit();
+ void set_only_update_transform_changes(bool p_enable);
+
public:
uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index 329382c034..7d5360c0e4 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -158,8 +158,8 @@ void Joint2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint2D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint2D::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bias", PROPERTY_HINT_RANGE, "0,0.9,0.001"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_collision"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
}
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 9a44eb31bb..f93c7d1f79 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -59,14 +59,22 @@ bool Light2D::_edit_use_pivot() const {
Rect2 Light2D::_edit_get_rect() const {
if (texture.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = texture->get_size() * _scale;
return Rect2(texture_offset - s / 2.0, s);
}
bool Light2D::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 Light2D::get_anchorable_rect() const {
+ if (texture.is_null())
+ return Rect2();
+
+ Size2 s = texture->get_size() * _scale;
+ return Rect2(texture_offset - s / 2.0, s);
}
void Light2D::_update_light_visibility() {
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 543805e329..40469cfbc8 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -94,6 +94,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 3e61dd05f4..e9e895b5bb 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -349,7 +349,7 @@ void Line2D::_bind_methods() {
ADD_GROUP("Fill", "");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile"), "set_texture_mode", "get_texture_mode");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "set_texture_mode", "get_texture_mode");
ADD_GROUP("Capping", "");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode");
@@ -368,6 +368,7 @@ void Line2D::_bind_methods() {
BIND_ENUM_CONSTANT(LINE_TEXTURE_NONE);
BIND_ENUM_CONSTANT(LINE_TEXTURE_TILE);
+ BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH);
ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed);
}
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 24c48982cd..6918018c12 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -52,8 +52,8 @@ public:
enum LineTextureMode {
LINE_TEXTURE_NONE = 0,
- LINE_TEXTURE_TILE
- // TODO STRETCH mode
+ LINE_TEXTURE_TILE,
+ LINE_TEXTURE_STRETCH
};
Line2D();
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index 845788bada..74ad3e79d0 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -146,7 +146,9 @@ void LineBuilder::build() {
float current_distance1 = 0.f;
float total_distance = 0.f;
_interpolate_color = gradient != NULL;
- bool distance_required = _interpolate_color || texture_mode == Line2D::LINE_TEXTURE_TILE;
+ bool distance_required = _interpolate_color ||
+ texture_mode == Line2D::LINE_TEXTURE_TILE ||
+ texture_mode == Line2D::LINE_TEXTURE_STRETCH;
if (distance_required)
total_distance = calculate_total_distance(points);
if (_interpolate_color)
@@ -170,7 +172,7 @@ void LineBuilder::build() {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx0 = 0.5f / tile_aspect;
}
- new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f));
+ new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f));
total_distance += width;
current_distance0 += hw;
current_distance1 = current_distance0;
@@ -292,6 +294,9 @@ void LineBuilder::build() {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx0 = current_distance0 / (width * tile_aspect);
uvx1 = current_distance1 / (width * tile_aspect);
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx0 = current_distance0 / total_distance;
+ uvx1 = current_distance1 / total_distance;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
@@ -378,6 +383,8 @@ void LineBuilder::build() {
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx1 = current_distance1 / total_distance;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
@@ -386,7 +393,7 @@ void LineBuilder::build() {
if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
// Note: color is not used in case we don't interpolate...
Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
- new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f));
+ new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f));
}
}
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 3813bd96fe..7252602a93 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -130,7 +130,6 @@ void Node2D::_update_xform_values() {
void Node2D::_update_transform() {
- Transform2D mat(angle, pos);
_mat.set_rotation_and_scale(angle, _scale);
_mat.elements[2] = pos;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index feb11089d0..d5a61b0b6b 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -30,10 +30,11 @@
#include "physics_body_2d.h"
+#include "core/core_string_names.h"
#include "core/method_bind_ext.gen.inc"
#include "engine.h"
+#include "math_funcs.h"
#include "scene/scene_string_names.h"
-
void PhysicsBody2D::_notification(int p_what) {
/*
@@ -186,28 +187,75 @@ real_t StaticBody2D::get_constant_angular_velocity() const {
return constant_angular_velocity;
}
+#ifndef DISABLE_DEPRECATED
void StaticBody2D::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
+
real_t StaticBody2D::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void StaticBody2D::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
+
real_t StaticBody2D::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const {
+ return physics_material_override;
}
void StaticBody2D::_bind_methods() {
@@ -222,23 +270,41 @@ void StaticBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody2D::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody2D::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody2D::_reload_physics_characteristics);
+
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
}
StaticBody2D::StaticBody2D() :
PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) {
constant_angular_velocity = 0;
- bounce = 0;
- friction = 1;
}
StaticBody2D::~StaticBody2D() {
}
+void StaticBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT);
+ } else {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode());
+ }
+}
+
void RigidBody2D::_body_enter_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
@@ -545,28 +611,72 @@ real_t RigidBody2D::get_weight() const {
return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10;
}
+#ifndef DISABLE_DEPRECATED
void RigidBody2D::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
real_t RigidBody2D::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void RigidBody2D::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
real_t RigidBody2D::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const {
+ return physics_material_override;
}
void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) {
@@ -843,6 +953,11 @@ void RigidBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody2D::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody2D::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody2D::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale);
@@ -903,6 +1018,7 @@ void RigidBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode");
@@ -941,9 +1057,7 @@ RigidBody2D::RigidBody2D() :
mode = MODE_RIGID;
- bounce = 0;
mass = 1;
- friction = 1;
gravity_scale = 1;
linear_damp = -1;
@@ -969,13 +1083,27 @@ RigidBody2D::~RigidBody2D() {
memdelete(contact_monitor);
}
+void RigidBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT);
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT);
+ } else {
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode());
+ Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode());
+ }
+}
+
//////////////////////////
-Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) {
+Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) {
Collision col;
- if (move_and_collide(p_motion, p_infinite_inertia, col)) {
+ if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) {
if (motion_cache.is_null()) {
motion_cache.instance();
motion_cache->owner = this;
@@ -989,11 +1117,48 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p
return Ref<KinematicCollision2D>();
}
-bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) {
+bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) {
+
+ Physics2DServer::SeparationResult sep_res[8]; //max 8 rays
+
+ Transform2D gt = get_global_transform();
+
+ Vector2 recover;
+ int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin);
+ int deepest = -1;
+ float deepest_depth;
+ for (int i = 0; i < hits; i++) {
+ if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) {
+ deepest = i;
+ deepest_depth = sep_res[i].collision_depth;
+ }
+ }
+
+ gt.elements[2] += recover;
+ set_global_transform(gt);
+
+ if (deepest != -1) {
+ r_collision.collider = sep_res[deepest].collider_id;
+ r_collision.collider_metadata = sep_res[deepest].collider_metadata;
+ r_collision.collider_shape = sep_res[deepest].collider_shape;
+ r_collision.collider_vel = sep_res[deepest].collider_velocity;
+ r_collision.collision = sep_res[deepest].collision_point;
+ r_collision.normal = sep_res[deepest].collision_normal;
+ r_collision.local_shape = sep_res[deepest].collision_local_shape;
+ r_collision.travel = recover;
+ r_collision.remainder = Vector2();
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
Transform2D gt = get_global_transform();
Physics2DServer::MotionResult result;
- bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result);
+ bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes);
if (colliding) {
r_collision.collider_metadata = result.collider_metadata;
@@ -1002,23 +1167,36 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
r_collision.collision = result.collision_point;
r_collision.normal = result.collision_normal;
r_collision.collider = result.collider_id;
+ r_collision.collider_rid = result.collider;
r_collision.travel = result.motion;
r_collision.remainder = result.remainder;
r_collision.local_shape = result.collision_local_shape;
}
- gt.elements[2] += result.motion;
- set_global_transform(gt);
+ if (!p_test_only) {
+ gt.elements[2] += result.motion;
+ set_global_transform(gt);
+ }
return colliding;
}
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
- Vector2 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time();
+ Vector2 floor_motion = floor_velocity;
+ if (on_floor && on_floor_body.is_valid()) {
+ //this approach makes sure there is less delay between the actual body velocity and the one we saved
+ Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
+ if (bs) {
+ floor_motion = bs->get_linear_velocity();
+ }
+ }
+
+ Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time();
Vector2 lv = p_linear_velocity;
on_floor = false;
+ on_floor_body = RID();
on_ceiling = false;
on_wall = false;
colliders.clear();
@@ -1027,48 +1205,68 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
while (p_max_slides) {
Collision collision;
+ bool found_collision = false;
+
+ for (int i = 0; i < 2; i++) {
+ bool collided;
+ if (i == 0) { //collide
+ collided = move_and_collide(motion, p_infinite_inertia, collision);
+ if (!collided) {
+ motion = Vector2(); //clear because no collision happened and motion completed
+ }
+ } else { //separate raycasts (if any)
+ collided = separate_raycast_shapes(p_infinite_inertia, collision);
+ if (collided) {
+ collision.remainder = motion; //keep
+ collision.travel = Vector2();
+ }
+ }
- bool collided = move_and_collide(motion, p_infinite_inertia, collision);
-
- if (collided) {
-
- motion = collision.remainder;
-
- if (p_floor_direction == Vector2()) {
- //all is a wall
- on_wall = true;
- } else {
- if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+ if (collided) {
+ found_collision = true;
+ }
- on_floor = true;
- floor_velocity = collision.collider_vel;
+ if (collided) {
- Vector2 rel_v = lv - floor_velocity;
- Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v);
+ motion = collision.remainder;
- if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) {
- Transform2D gt = get_global_transform();
- gt.elements[2] -= collision.travel;
- set_global_transform(gt);
- return Vector2();
- }
- } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
- on_ceiling = true;
- } else {
+ if (p_floor_direction == Vector2()) {
+ //all is a wall
on_wall = true;
+ } else {
+ if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+
+ on_floor = true;
+ on_floor_body = collision.collider_rid;
+ floor_velocity = collision.collider_vel;
+
+ Vector2 rel_v = lv - floor_velocity;
+ Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v);
+
+ if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) {
+ Transform2D gt = get_global_transform();
+ gt.elements[2] -= collision.travel;
+ set_global_transform(gt);
+ return Vector2();
+ }
+ } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
+ on_ceiling = true;
+ } else {
+ on_wall = true;
+ }
}
- }
- Vector2 n = collision.normal;
- motion = motion.slide(n);
- lv = lv.slide(n);
+ Vector2 n = collision.normal;
+ motion = motion.slide(n);
+ lv = lv.slide(n);
- colliders.push_back(collision);
+ colliders.push_back(collision);
+ }
+ }
- } else {
+ if (!found_collision) {
break;
}
-
p_max_slides--;
if (motion == Vector2())
break;
@@ -1077,6 +1275,31 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return lv;
}
+Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
+
+ bool was_on_floor = on_floor;
+
+ Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_slope_stop_min_velocity, p_max_slides, p_floor_max_angle);
+ if (!was_on_floor || p_snap == Vector2()) {
+ return ret;
+ }
+
+ Collision col;
+ Transform2D gt = get_global_transform();
+
+ if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) {
+ gt.elements[2] += col.travel;
+ if (p_floor_direction != Vector2() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) {
+ on_floor = true;
+ on_floor_body = col.collider_rid;
+ floor_velocity = col.collider_vel;
+ }
+ set_global_transform(gt);
+ }
+
+ return ret;
+}
+
bool KinematicBody2D::is_on_floor() const {
return on_floor;
@@ -1138,10 +1361,60 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce];
}
+void KinematicBody2D::set_sync_to_physics(bool p_enable) {
+
+ if (sync_to_physics == p_enable) {
+ return;
+ }
+ sync_to_physics = p_enable;
+ if (p_enable) {
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
+ set_only_update_transform_changes(true);
+ set_notify_local_transform(true);
+ } else {
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
+ }
+}
+
+bool KinematicBody2D::is_sync_to_physics_enabled() const {
+ return sync_to_physics;
+}
+
+void KinematicBody2D::_direct_state_changed(Object *p_state) {
+
+ if (!sync_to_physics)
+ return;
+
+ Physics2DDirectBodyState *state = Object::cast_to<Physics2DDirectBodyState>(p_state);
+
+ last_valid_transform = state->get_transform();
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
+}
+
+void KinematicBody2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ last_valid_transform = get_global_transform();
+ }
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ //used by sync to physics, send the new transform to the physics
+ Transform2D new_transform = get_global_transform();
+ Physics2DServer::get_singleton()->body_set_state(get_rid(), Physics2DServer::BODY_STATE_TRANSFORM, new_transform);
+ //but then revert changes
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
+ }
+}
void KinematicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move);
@@ -1156,7 +1429,13 @@ void KinematicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody2D::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody2D::_get_slide_collision);
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled);
+
+ ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody2D::_direct_state_changed);
+
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}
KinematicBody2D::KinematicBody2D() :
@@ -1167,6 +1446,7 @@ KinematicBody2D::KinematicBody2D() :
on_floor = false;
on_ceiling = false;
on_wall = false;
+ sync_to_physics = false;
}
KinematicBody2D::~KinematicBody2D() {
if (motion_cache.is_valid()) {
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 0fda3c5c05..bd100f6228 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -32,6 +32,7 @@
#define PHYSICS_BODY_2D_H
#include "scene/2d/collision_object_2d.h"
+#include "scene/resources/physics_material.h"
#include "servers/physics_2d_server.h"
#include "vset.h"
@@ -79,18 +80,21 @@ class StaticBody2D : public PhysicsBody2D {
Vector2 constant_linear_velocity;
real_t constant_angular_velocity;
- real_t bounce;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
protected:
static void _bind_methods();
public:
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_constant_linear_velocity(const Vector2 &p_vel);
void set_constant_angular_velocity(real_t p_vel);
@@ -100,6 +104,9 @@ public:
StaticBody2D();
~StaticBody2D();
+
+private:
+ void _reload_physics_characteristics();
};
class RigidBody2D : public PhysicsBody2D {
@@ -125,9 +132,8 @@ private:
Physics2DDirectBodyState *state;
Mode mode;
- real_t bounce;
real_t mass;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
real_t gravity_scale;
real_t linear_damp;
real_t angular_damp;
@@ -204,11 +210,16 @@ public:
void set_weight(real_t p_weight);
real_t get_weight() const;
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
@@ -261,6 +272,9 @@ public:
RigidBody2D();
~RigidBody2D();
+
+private:
+ void _reload_physics_characteristics();
};
VARIANT_ENUM_CAST(RigidBody2D::Mode);
@@ -276,6 +290,7 @@ public:
Vector2 normal;
Vector2 collider_vel;
ObjectID collider;
+ RID collider_rid;
int collider_shape;
Variant collider_metadata;
Vector2 remainder;
@@ -287,29 +302,40 @@ private:
float margin;
Vector2 floor_velocity;
+ RID on_floor_body;
bool on_floor;
bool on_ceiling;
bool on_wall;
+ bool sync_to_physics;
+
Vector<Collision> colliders;
Vector<Ref<KinematicCollision2D> > slide_colliders;
Ref<KinematicCollision2D> motion_cache;
_FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const;
- Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true);
+ Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
+ Transform2D last_valid_transform;
+ void _direct_state_changed(Object *p_state);
+
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
- bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision);
+ bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia);
+ bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision);
+
void set_safe_margin(float p_margin);
float get_safe_margin() const;
Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
@@ -318,6 +344,9 @@ public:
int get_slide_count() const;
Collision get_slide_collision(int p_bounce) const;
+ void set_sync_to_physics(bool p_enable);
+ bool is_sync_to_physics_enabled() const;
+
KinematicBody2D();
~KinematicBody2D();
};
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 4d6ebc81c3..81ed3c63c3 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -649,7 +649,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
ADD_GROUP("Skeleton", "");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton");
ADD_GROUP("Invert", "invert_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert");
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index da764e032b..63c3d78dfd 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -201,7 +201,7 @@ void RemoteTransform2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_scale", "update_remote_scale"), &RemoteTransform2D::set_update_scale);
ClassDB::bind_method(D_METHOD("get_update_scale"), &RemoteTransform2D::get_update_scale);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path"), "set_remote_node", "get_remote_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_remote_node", "get_remote_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_coordinates"), "set_use_global_coordinates", "get_use_global_coordinates");
ADD_GROUP("Update", "update_");
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index 9480f18176..45f63fd5bf 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -324,19 +324,21 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
}
Rect2 TouchScreenButton::_edit_get_rect() const {
-
- if (texture.is_null())
- return Rect2(0, 0, 1, 1);
- /*
if (texture.is_null())
return CanvasItem::_edit_get_rect();
- */
return Rect2(Size2(), texture->get_size());
}
bool TouchScreenButton::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 TouchScreenButton::get_anchorable_rect() const {
+ if (texture.is_null())
+ return CanvasItem::get_anchorable_rect();
+
+ return Rect2(Size2(), texture->get_size());
}
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
index b2fafcc93d..3e8adc2e5e 100644
--- a/scene/2d/screen_button.h
+++ b/scene/2d/screen_button.h
@@ -103,8 +103,9 @@ public:
bool is_pressed() const;
- Rect2 _edit_get_rect() const;
+ virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
TouchScreenButton();
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 64d0771fab..ebe0e81f6e 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -63,9 +63,16 @@ Rect2 Sprite::_edit_get_rect() const {
}
bool Sprite::_edit_use_rect() const {
+ if (texture.is_null())
+ return false;
+
return true;
}
+Rect2 Sprite::get_anchorable_rect() const {
+ return get_rect();
+}
+
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
Rect2 base_rect;
@@ -367,10 +374,6 @@ Rect2 Sprite::get_rect() const {
if (texture.is_null())
return Rect2(0, 0, 1, 1);
- /*
- if (texture.is_null())
- return CanvasItem::_edit_get_rect();
- */
Size2i s;
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index 609ad8bb34..0a5ff002cd 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -115,6 +115,7 @@ public:
int get_hframes() const;
Rect2 get_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
Sprite();
~Sprite();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 98275510d6..72c3ed7425 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -62,7 +62,7 @@ void TileMap::_notification(int p_what) {
pending_update = true;
_recreate_quadrants();
- _update_dirty_quadrants();
+ update_dirty_quadrants();
RID space = get_world_2d()->get_space();
_update_quadrant_transform();
_update_quadrant_space(space);
@@ -245,7 +245,7 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
xform.elements[2].y += offset.y;
}
-void TileMap::_update_dirty_quadrants() {
+void TileMap::update_dirty_quadrants() {
if (!pending_update)
return;
@@ -708,7 +708,7 @@ void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
rect_cache_dirty = true;
}
-void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q) {
+void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update) {
Quadrant &q = Q->get();
if (!q.dirty_list.in_list())
@@ -719,7 +719,10 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q) {
pending_update = true;
if (!is_inside_tree())
return;
- _update_dirty_quadrants();
+
+ if (update) {
+ call_deferred("update_dirty_quadrants");
+ }
}
void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose) {
@@ -727,6 +730,11 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_
set_cell(p_pos.x, p_pos.y, p_tile, p_flip_x, p_flip_y, p_transpose);
}
+void TileMap::set_celld(const Vector2 &p_pos, const Dictionary &p_data) {
+
+ set_cell(p_pos.x, p_pos.y, p_data["id"], p_data["flip_h"], p_data["flip_y"], p_data["transpose"], p_data["auto_coord"]);
+}
+
void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) {
PosKey pk(p_x, p_y);
@@ -1016,8 +1024,9 @@ void TileMap::_recreate_quadrants() {
}
Q->get().cells.insert(E->key());
- _make_quadrant_dirty(Q);
+ _make_quadrant_dirty(Q, false);
}
+ update_dirty_quadrants();
}
void TileMap::_clear_quadrants() {
@@ -1143,16 +1152,6 @@ PoolVector<int> TileMap::_get_tile_data() const {
return data;
}
-Rect2 TileMap::_edit_get_rect() const {
-
- const_cast<TileMap *>(this)->_update_dirty_quadrants();
- return rect_cache;
-}
-
-bool TileMap::_edit_use_rect() const {
- return true;
-}
-
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
@@ -1612,6 +1611,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_celld", "data"), &TileMap::set_celld);
ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell);
ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv);
ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped);
@@ -1630,7 +1630,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_clear_quadrants"), &TileMap::_clear_quadrants);
ClassDB::bind_method(D_METHOD("_recreate_quadrants"), &TileMap::_recreate_quadrants);
- ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants);
+ ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants);
ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area);
ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2()));
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 07947004b3..c8aceac17d 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -188,10 +188,9 @@ private:
Map<PosKey, Quadrant>::Element *_create_quadrant(const PosKey &p_qk);
void _erase_quadrant(Map<PosKey, Quadrant>::Element *Q);
- void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q);
+ void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true);
void _recreate_quadrants();
void _clear_quadrants();
- void _update_dirty_quadrants();
void _update_quadrant_space(const RID &p_space);
void _update_quadrant_transform();
void _recompute_rect_cache();
@@ -241,18 +240,18 @@ public:
void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord);
Vector2 get_cell_autotile_coord(int p_x, int p_y) const;
+ void set_celld(const Vector2 &p_pos, const Dictionary &p_data);
void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false);
int get_cellv(const Vector2 &p_pos) const;
- Rect2 _edit_get_rect() const;
- virtual bool _edit_use_rect() const;
-
void make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2());
void update_cell_bitmask(int p_x, int p_y);
void update_dirty_bitmask();
+ void update_dirty_quadrants();
+
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp
index 001c58ea76..4bff26a200 100644
--- a/scene/3d/arvr_nodes.cpp
+++ b/scene/3d/arvr_nodes.cpp
@@ -73,7 +73,10 @@ Vector3 ARVRCamera::project_local_ray_normal(const Point2 &p_pos) const {
ERR_FAIL_NULL_V(arvr_server, Vector3());
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
- ERR_FAIL_COND_V(arvr_interface.is_null(), Vector3());
+ if (arvr_interface.is_null()) {
+ // we might be in the editor or have VR turned off, just call superclass
+ return Camera::project_local_ray_normal(p_pos);
+ }
if (!is_inside_tree()) {
ERR_EXPLAIN("Camera is not inside scene.");
@@ -98,7 +101,10 @@ Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const {
ERR_FAIL_NULL_V(arvr_server, Vector2());
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
- ERR_FAIL_COND_V(arvr_interface.is_null(), Vector2());
+ if (arvr_interface.is_null()) {
+ // we might be in the editor or have VR turned off, just call superclass
+ return Camera::unproject_position(p_pos);
+ }
if (!is_inside_tree()) {
ERR_EXPLAIN("Camera is not inside scene.");
@@ -127,7 +133,10 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const {
ERR_FAIL_NULL_V(arvr_server, Vector3());
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
- ERR_FAIL_COND_V(arvr_interface.is_null(), Vector3());
+ if (arvr_interface.is_null()) {
+ // we might be in the editor or have VR turned off, just call superclass
+ return Camera::project_position(p_point);
+ }
if (!is_inside_tree()) {
ERR_EXPLAIN("Camera is not inside scene.");
@@ -157,7 +166,10 @@ Vector<Plane> ARVRCamera::get_frustum() const {
ERR_FAIL_NULL_V(arvr_server, Vector<Plane>());
Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface();
- ERR_FAIL_COND_V(arvr_interface.is_null(), Vector<Plane>());
+ if (arvr_interface.is_null()) {
+ // we might be in the editor or have VR turned off, just call superclass
+ return Camera::get_frustum();
+ }
ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index e7b3645001..5f0ac3dd80 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -35,11 +35,8 @@
#include "scene/main/viewport.h"
void AudioStreamPlayer3D::_mix_audio() {
- if (!stream_playback.is_valid()) {
- return;
- }
-
- if (!active) {
+ if (!stream_playback.is_valid() || !active ||
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -54,8 +51,13 @@ void AudioStreamPlayer3D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- //mix
- if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) {
+ if (stream_paused_fade_out) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
+ // Mix if we're not paused or we're fading out
+ if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) {
float output_pitch_scale = 0.0;
if (output_count) {
@@ -105,8 +107,10 @@ void AudioStreamPlayer3D::_mix_audio() {
int buffers = AudioServer::get_singleton()->get_channel_count();
for (int k = 0; k < buffers; k++) {
- AudioFrame vol_inc = (current.vol[k] - prev_outputs[i].vol[k]) / float(buffer_size);
- AudioFrame vol = current.vol[k];
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
+ AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
@@ -188,6 +192,8 @@ void AudioStreamPlayer3D::_mix_audio() {
}
output_ready = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
@@ -237,6 +243,18 @@ void AudioStreamPlayer3D::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
+
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
@@ -552,7 +570,6 @@ void AudioStreamPlayer3D::_notification(int p_what) {
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
- ERR_FAIL_COND(!p_stream.is_valid());
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
@@ -564,14 +581,15 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
setseek = -1;
}
- stream = p_stream;
- stream_playback = p_stream->instance_playback();
+ if (p_stream.is_valid()) {
+ stream = p_stream;
+ stream_playback = p_stream->instance_playback();
+ }
AudioServer::get_singleton()->unlock();
- if (stream_playback.is_null()) {
+ if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -825,6 +843,20 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
return doppler_tracking;
}
+void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade_in = stream_paused ? false : true;
+ stream_paused_fade_out = stream_paused ? true : false;
+ }
+}
+
+bool AudioStreamPlayer3D::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
@@ -888,6 +920,9 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &AudioStreamPlayer3D::set_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &AudioStreamPlayer3D::get_doppler_tracking);
+ ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
+
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -898,6 +933,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -949,9 +985,13 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() {
attenuation_filter_db = -24;
out_of_range_mode = OUT_OF_RANGE_MIX;
doppler_tracking = DOPPLER_TRACKING_DISABLED;
+ stream_paused = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
velocity_tracker.instance();
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
+ set_disable_scale(true);
}
AudioStreamPlayer3D::~AudioStreamPlayer3D() {
}
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 1fcb83cf21..14413d0702 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -108,6 +108,9 @@ private:
float max_db;
float pitch_scale;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -199,6 +202,9 @@ public:
void set_doppler_tracking(DopplerTracking p_tracking);
DopplerTracking get_doppler_tracking() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer3D();
~AudioStreamPlayer3D();
};
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index 204aaef7ec..26fd5ed658 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -811,4 +811,5 @@ BakedLightmap::BakedLightmap() {
propagation = 1;
hdr = false;
image_path = ".";
+ set_disable_scale(true);
}
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index e11e8abe5b..9a2046991b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -613,6 +613,7 @@ Camera::Camera() {
velocity_tracker.instance();
doppler_tracking = DOPPLER_TRACKING_DISABLED;
set_notify_transform(true);
+ set_disable_scale(true);
}
Camera::~Camera() {
diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h
index f31d65e411..f8ef04b78f 100644
--- a/scene/3d/collision_object.h
+++ b/scene/3d/collision_object.h
@@ -39,6 +39,7 @@ class CollisionObject : public Spatial {
GDCLASS(CollisionObject, Spatial);
bool area;
+
RID rid;
struct ShapeData {
diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h
index c9c91a5824..6ca8e80ea1 100644
--- a/scene/3d/collision_shape.h
+++ b/scene/3d/collision_shape.h
@@ -49,6 +49,7 @@ class CollisionShape : public Spatial {
void resource_changed(RES res);
bool disabled;
+protected:
void _create_debug_shape();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
new file mode 100644
index 0000000000..c3b0196316
--- /dev/null
+++ b/scene/3d/cpu_particles.cpp
@@ -0,0 +1,1417 @@
+#include "cpu_particles.h"
+
+#include "particles.h"
+#include "scene/3d/camera.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/surface_tool.h"
+#include "servers/visual_server.h"
+
+AABB CPUParticles::get_aabb() const {
+
+ return AABB();
+}
+PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const {
+
+ return PoolVector<Face3>();
+}
+
+void CPUParticles::set_emitting(bool p_emitting) {
+
+ emitting = p_emitting;
+ if (!is_processing_internal()) {
+ set_process_internal(true);
+ if (is_inside_tree()) {
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, true);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+}
+
+void CPUParticles::set_amount(int p_amount) {
+
+ ERR_FAIL_COND(p_amount < 1);
+
+ particles.resize(p_amount);
+ {
+ PoolVector<Particle>::Write w = particles.write();
+
+ for (int i = 0; i < p_amount; i++) {
+ w[i].active = false;
+ }
+ }
+
+ particle_data.resize((12 + 4 + 1) * p_amount);
+ VS::get_singleton()->multimesh_allocate(multimesh, p_amount, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_8BIT, VS::MULTIMESH_CUSTOM_DATA_FLOAT);
+
+ particle_order.resize(p_amount);
+}
+void CPUParticles::set_lifetime(float p_lifetime) {
+
+ ERR_FAIL_COND(p_lifetime <= 0);
+ lifetime = p_lifetime;
+}
+
+void CPUParticles::set_one_shot(bool p_one_shot) {
+
+ one_shot = p_one_shot;
+}
+
+void CPUParticles::set_pre_process_time(float p_time) {
+
+ pre_process_time = p_time;
+}
+void CPUParticles::set_explosiveness_ratio(float p_ratio) {
+
+ explosiveness_ratio = p_ratio;
+}
+void CPUParticles::set_randomness_ratio(float p_ratio) {
+
+ randomness_ratio = p_ratio;
+}
+void CPUParticles::set_use_local_coordinates(bool p_enable) {
+
+ local_coords = p_enable;
+}
+void CPUParticles::set_speed_scale(float p_scale) {
+
+ speed_scale = p_scale;
+}
+
+bool CPUParticles::is_emitting() const {
+
+ return emitting;
+}
+int CPUParticles::get_amount() const {
+
+ return particles.size();
+}
+float CPUParticles::get_lifetime() const {
+
+ return lifetime;
+}
+bool CPUParticles::get_one_shot() const {
+
+ return one_shot;
+}
+
+float CPUParticles::get_pre_process_time() const {
+
+ return pre_process_time;
+}
+float CPUParticles::get_explosiveness_ratio() const {
+
+ return explosiveness_ratio;
+}
+float CPUParticles::get_randomness_ratio() const {
+
+ return randomness_ratio;
+}
+
+bool CPUParticles::get_use_local_coordinates() const {
+
+ return local_coords;
+}
+
+float CPUParticles::get_speed_scale() const {
+
+ return speed_scale;
+}
+
+void CPUParticles::set_draw_order(DrawOrder p_order) {
+
+ draw_order = p_order;
+}
+
+CPUParticles::DrawOrder CPUParticles::get_draw_order() const {
+
+ return draw_order;
+}
+
+void CPUParticles::set_mesh(const Ref<Mesh> &p_mesh) {
+
+ mesh = p_mesh;
+ if (mesh.is_valid()) {
+ VS::get_singleton()->multimesh_set_mesh(multimesh, mesh->get_rid());
+ } else {
+ VS::get_singleton()->multimesh_set_mesh(multimesh, RID());
+ }
+}
+
+Ref<Mesh> CPUParticles::get_mesh() const {
+
+ return mesh;
+}
+
+void CPUParticles::set_fixed_fps(int p_count) {
+ fixed_fps = p_count;
+}
+
+int CPUParticles::get_fixed_fps() const {
+ return fixed_fps;
+}
+
+void CPUParticles::set_fractional_delta(bool p_enable) {
+ fractional_delta = p_enable;
+}
+
+bool CPUParticles::get_fractional_delta() const {
+ return fractional_delta;
+}
+
+String CPUParticles::get_configuration_warning() const {
+
+ String warnings;
+
+ return warnings;
+}
+
+void CPUParticles::restart() {
+
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+
+ {
+ int pc = particles.size();
+ PoolVector<Particle>::Write w = particles.write();
+
+ for (int i = 0; i < pc; i++) {
+ w[i].active = false;
+ }
+ }
+}
+
+void CPUParticles::set_spread(float p_spread) {
+
+ spread = p_spread;
+}
+
+float CPUParticles::get_spread() const {
+
+ return spread;
+}
+
+void CPUParticles::set_flatness(float p_flatness) {
+
+ flatness = p_flatness;
+}
+float CPUParticles::get_flatness() const {
+
+ return flatness;
+}
+
+void CPUParticles::set_param(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ parameters[p_param] = p_value;
+}
+float CPUParticles::get_param(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
+
+ return parameters[p_param];
+}
+
+void CPUParticles::set_param_randomness(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ randomness[p_param] = p_value;
+}
+float CPUParticles::get_param_randomness(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
+
+ return randomness[p_param];
+}
+
+static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) {
+
+ Ref<Curve> curve = p_curve;
+ if (!curve.is_valid())
+ return;
+
+ curve->ensure_default_setup(p_min, p_max);
+}
+
+void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+
+ curve_parameters[p_param] = p_curve;
+
+ switch (p_param) {
+ case PARAM_INITIAL_LINEAR_VELOCITY: {
+ //do none for this one
+ } break;
+ case PARAM_ANGULAR_VELOCITY: {
+ _adjust_curve_range(p_curve, -360, 360);
+ } break;
+ /*case PARAM_ORBIT_VELOCITY: {
+ _adjust_curve_range(p_curve, -500, 500);
+ } break;*/
+ case PARAM_LINEAR_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_RADIAL_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_TANGENTIAL_ACCEL: {
+ _adjust_curve_range(p_curve, -200, 200);
+ } break;
+ case PARAM_DAMPING: {
+ _adjust_curve_range(p_curve, 0, 100);
+ } break;
+ case PARAM_ANGLE: {
+ _adjust_curve_range(p_curve, -360, 360);
+ } break;
+ case PARAM_SCALE: {
+
+ } break;
+ case PARAM_HUE_VARIATION: {
+ _adjust_curve_range(p_curve, -1, 1);
+ } break;
+ case PARAM_ANIM_SPEED: {
+ _adjust_curve_range(p_curve, 0, 200);
+ } break;
+ case PARAM_ANIM_OFFSET: {
+ } break;
+ default: {}
+ }
+}
+Ref<Curve> CPUParticles::get_param_curve(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Curve>());
+
+ return curve_parameters[p_param];
+}
+
+void CPUParticles::set_color(const Color &p_color) {
+
+ color = p_color;
+}
+
+Color CPUParticles::get_color() const {
+
+ return color;
+}
+
+void CPUParticles::set_color_ramp(const Ref<Gradient> &p_ramp) {
+
+ color_ramp = p_ramp;
+}
+
+Ref<Gradient> CPUParticles::get_color_ramp() const {
+
+ return color_ramp;
+}
+
+void CPUParticles::set_particle_flag(Flags p_flag, bool p_enable) {
+ ERR_FAIL_INDEX(p_flag, FLAG_MAX);
+ flags[p_flag] = p_enable;
+ if (p_flag == FLAG_DISABLE_Z) {
+ _change_notify();
+ }
+}
+
+bool CPUParticles::get_particle_flag(Flags p_flag) const {
+ ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
+ return flags[p_flag];
+}
+
+void CPUParticles::set_emission_shape(EmissionShape p_shape) {
+
+ emission_shape = p_shape;
+}
+
+void CPUParticles::set_emission_sphere_radius(float p_radius) {
+
+ emission_sphere_radius = p_radius;
+}
+
+void CPUParticles::set_emission_box_extents(Vector3 p_extents) {
+
+ emission_box_extents = p_extents;
+}
+
+void CPUParticles::set_emission_points(const PoolVector<Vector3> &p_points) {
+
+ emission_points = p_points;
+}
+
+void CPUParticles::set_emission_normals(const PoolVector<Vector3> &p_normals) {
+
+ emission_normals = p_normals;
+}
+
+void CPUParticles::set_emission_colors(const PoolVector<Color> &p_colors) {
+
+ emission_colors = p_colors;
+}
+
+float CPUParticles::get_emission_sphere_radius() const {
+
+ return emission_sphere_radius;
+}
+Vector3 CPUParticles::get_emission_box_extents() const {
+
+ return emission_box_extents;
+}
+PoolVector<Vector3> CPUParticles::get_emission_points() const {
+
+ return emission_points;
+}
+PoolVector<Vector3> CPUParticles::get_emission_normals() const {
+
+ return emission_normals;
+}
+
+PoolVector<Color> CPUParticles::get_emission_colors() const {
+
+ return emission_colors;
+}
+
+CPUParticles::EmissionShape CPUParticles::get_emission_shape() const {
+ return emission_shape;
+}
+void CPUParticles::set_gravity(const Vector3 &p_gravity) {
+
+ gravity = p_gravity;
+}
+
+Vector3 CPUParticles::get_gravity() const {
+
+ return gravity;
+}
+
+void CPUParticles::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "color" && color_ramp.is_valid()) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) {
+ property.usage = 0;
+ }
+
+ if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
+ property.usage = 0;
+ }
+ /*
+ if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
+ property.usage = 0;
+ }
+ */
+}
+
+static uint32_t idhash(uint32_t x) {
+
+ x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b);
+ x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b);
+ x = (x >> uint32_t(16)) ^ x;
+ return x;
+}
+
+static float rand_from_seed(uint32_t &seed) {
+ int k;
+ int s = int(seed);
+ if (s == 0)
+ s = 305420679;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ seed = uint32_t(s);
+ return float(seed % uint32_t(65536)) / 65535.0;
+}
+
+float rand_from_seed_m1_p1(uint32_t &seed) {
+ return rand_from_seed(seed) * 2.0 - 1.0;
+}
+
+void CPUParticles::_particles_process(float p_delta) {
+
+ p_delta *= speed_scale;
+
+ int pcount = particles.size();
+ PoolVector<Particle>::Write w = particles.write();
+
+ Particle *parray = w.ptr();
+
+ float prev_time = time;
+ time += p_delta;
+ if (time > lifetime) {
+ time = Math::fmod(time, lifetime);
+ cycle++;
+ if (one_shot && cycle > 0) {
+ emitting = false;
+ }
+ }
+
+ Transform emission_xform;
+ Basis velocity_xform;
+ if (!local_coords) {
+ emission_xform = get_global_transform();
+ velocity_xform = emission_xform.basis.inverse().transposed();
+ }
+
+ for (int i = 0; i < pcount; i++) {
+
+ Particle &p = parray[i];
+
+ if (!emitting && !p.active)
+ continue;
+
+ float restart_time = (float(i) / float(pcount)) * lifetime;
+ float local_delta = p_delta;
+
+ if (randomness_ratio > 0.0) {
+ uint32_t seed = cycle;
+ if (restart_time >= time) {
+ seed -= uint32_t(1);
+ }
+ seed *= uint32_t(pcount);
+ seed += uint32_t(i);
+ float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
+ restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ }
+
+ restart_time *= (1.0 - explosiveness_ratio);
+ bool restart = false;
+
+ if (time > prev_time) {
+ // restart_time >= prev_time is used so particles emit in the first frame they are processed
+
+ if (restart_time >= prev_time && restart_time < time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (time - restart_time) * lifetime;
+ }
+ }
+
+ } else if (local_delta > 0.0) {
+ if (restart_time >= prev_time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (1.0 - restart_time + time) * lifetime;
+ }
+
+ } else if (restart_time < time) {
+ restart = true;
+ if (fractional_delta) {
+ local_delta = (time - restart_time) * lifetime;
+ }
+ }
+ }
+
+ if (restart) {
+
+ if (!emitting) {
+ p.active = false;
+ continue;
+ }
+ p.active = true;
+
+ /*float tex_linear_velocity = 0;
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0);
+ }*/
+
+ float tex_angle = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(0);
+ }
+
+ float tex_anim_offset = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(0);
+ }
+
+ p.seed = Math::rand();
+
+ p.angle_rand = Math::randf();
+ p.scale_rand = Math::randf();
+ p.hue_rot_rand = Math::randf();
+ p.anim_offset_rand = Math::randf();
+
+ float angle1_rad;
+ float angle2_rad;
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
+ Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0);
+ p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
+
+ } else {
+ //initiate velocity spread in 3D
+ angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
+ angle2_rad = (Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * Math_PI * spread / 180.0;
+
+ Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad));
+ Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad));
+ direction_yz.z = direction_yz.z / Math::sqrt(direction_yz.z); //better uniform distribution
+ Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);
+ direction.normalize();
+ p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
+ }
+
+ float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
+ p.custom[0] = Math::deg2rad(base_angle); //angle
+ p.custom[1] = 0.0; //phase
+ p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1)
+ p.transform = Transform();
+ p.time = 0;
+ p.base_color = Color(1, 1, 1, 1);
+
+ switch (emission_shape) {
+ case EMISSION_SHAPE_POINT: {
+ //do none
+ } break;
+ case EMISSION_SHAPE_SPHERE: {
+ p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius;
+ } break;
+ case EMISSION_SHAPE_BOX: {
+ p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_box_extents;
+ } break;
+ case EMISSION_SHAPE_POINTS:
+ case EMISSION_SHAPE_DIRECTED_POINTS: {
+
+ int pc = emission_points.size();
+ if (pc == 0)
+ break;
+
+ int random_idx = Math::rand() % pc;
+
+ p.transform.origin = emission_points.get(random_idx);
+
+ if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) {
+ if (flags[FLAG_DISABLE_Z]) {
+ /*
+ mat2 rotm;
+ ";
+ rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;
+ rotm[1] = rotm[0].yx * vec2(1.0, -1.0);
+ VELOCITY.xy = rotm * VELOCITY.xy;
+ */
+ } else {
+ Vector3 normal = emission_normals.get(random_idx);
+ Vector3 v0 = Math::abs(normal.z) < 0.999 ? Vector3(0.0, 0.0, 1.0) : Vector3(0, 1.0, 0.0);
+ Vector3 tangent = v0.cross(normal).normalized();
+ Vector3 bitangent = tangent.cross(normal).normalized();
+ Basis m3;
+ m3.set_axis(0, tangent);
+ m3.set_axis(1, bitangent);
+ m3.set_axis(2, normal);
+ p.velocity = m3.xform(p.velocity);
+ }
+ }
+
+ if (emission_colors.size() == pc) {
+ p.base_color = emission_colors.get(random_idx);
+ }
+ } break;
+ }
+
+ if (!local_coords) {
+ p.velocity = velocity_xform.xform(p.velocity);
+ p.transform = emission_xform * p.transform;
+ }
+
+ if (flags[FLAG_DISABLE_Z]) {
+ p.velocity.z = 0.0;
+ p.velocity.z = 0.0;
+ }
+
+ } else if (!p.active) {
+ continue;
+ } else {
+
+ uint32_t alt_seed = p.seed;
+
+ p.time += local_delta;
+ p.custom[1] = p.time / lifetime;
+
+ float tex_linear_velocity = 0.0;
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
+ }
+ /*
+ float tex_orbit_velocity = 0.0;
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
+ }
+ }
+*/
+ float tex_angular_velocity = 0.0;
+ if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
+ tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
+ }
+
+ float tex_linear_accel = 0.0;
+ if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
+ tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_tangential_accel = 0.0;
+ if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
+ tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_radial_accel = 0.0;
+ if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
+ tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(p.custom[1]);
+ }
+
+ float tex_damping = 0.0;
+ if (curve_parameters[PARAM_DAMPING].is_valid()) {
+ tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(p.custom[1]);
+ }
+
+ float tex_angle = 0.0;
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
+ tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(p.custom[1]);
+ }
+ float tex_anim_speed = 0.0;
+ if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) {
+ tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(p.custom[1]);
+ }
+
+ float tex_anim_offset = 0.0;
+ if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) {
+ tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(p.custom[1]);
+ }
+
+ Vector3 force = gravity;
+ Vector3 pos = p.transform.origin;
+ if (flags[FLAG_DISABLE_Z]) {
+ pos.z = 0.0;
+ }
+ //apply linear acceleration
+ force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3();
+ //apply radial acceleration
+ Vector3 org = emission_xform.origin;
+ Vector3 diff = pos - org;
+ force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3();
+ //apply tangential acceleration;
+ if (flags[FLAG_DISABLE_Z]) {
+
+ Vector3 yx = Vector3(diff.y, 0, diff.x);
+ force += yx.length() > 0.0 ? (yx * Vector3(-1.0, 0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
+
+ } else {
+ Vector3 crossDiff = diff.normalized().cross(gravity.normalized());
+ force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
+ }
+ //apply attractor forces
+ p.velocity += force * local_delta;
+ //orbit velocity
+#if 0
+ if (flags[FLAG_DISABLE_Z]) {
+
+ float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
+ if (orbit_amount != 0.0) {
+ float ang = orbit_amount * DELTA * pi * 2.0;
+ mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
+ TRANSFORM[3].xy -= diff.xy;
+ TRANSFORM[3].xy += rot * diff.xy;
+ }
+ }
+#endif
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ p.velocity = p.velocity.normalized() * tex_linear_velocity;
+ }
+ if (parameters[PARAM_DAMPING] + tex_damping > 0.0) {
+
+ float v = p.velocity.length();
+ float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]);
+ v -= damp * local_delta;
+ if (v < 0.0) {
+ p.velocity = Vector3();
+ } else {
+ p.velocity = p.velocity.normalized() * v;
+ }
+ }
+ float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
+ base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
+ p.custom[0] = Math::deg2rad(base_angle); //angle
+ p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
+ if (flags[FLAG_ANIM_LOOP]) {
+ p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
+
+ } else {
+ p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
+ }
+ }
+ //apply color
+ //apply hue rotation
+
+ float tex_scale = 1.0;
+ if (curve_parameters[PARAM_SCALE].is_valid()) {
+ tex_scale = curve_parameters[PARAM_SCALE]->interpolate(p.custom[1]);
+ }
+
+ float tex_hue_variation = 0.0;
+ if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) {
+ tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(p.custom[1]);
+ }
+
+ float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_PI * 2.0 * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]);
+ float hue_rot_c = Math::cos(hue_rot_angle);
+ float hue_rot_s = Math::sin(hue_rot_angle);
+
+ Basis hue_rot_mat;
+ {
+ Basis mat1(0.299, 0.587, 0.114, 0.299, 0.587, 0.114, 0.299, 0.587, 0.114);
+ Basis mat2(0.701, -0.587, -0.114, -0.299, 0.413, -0.114, -0.300, -0.588, 0.886);
+ Basis mat3(0.168, 0.330, -0.497, -0.328, 0.035, 0.292, 1.250, -1.050, -0.203);
+
+ for (int j = 0; j < 3; j++) {
+ hue_rot_mat[j] = mat1[j] + mat2[j] * hue_rot_c + mat3[j] * hue_rot_s;
+ }
+ }
+
+ if (color_ramp.is_valid()) {
+ p.color = color_ramp->get_color_at_offset(p.custom[1]) * color;
+ } else {
+ p.color = color;
+ }
+
+ Vector3 color_rgb = hue_rot_mat.xform_inv(Vector3(p.color.r, p.color.g, p.color.b));
+ p.color.r = color_rgb.x;
+ p.color.g = color_rgb.y;
+ p.color.b = color_rgb.z;
+
+ p.color *= p.base_color;
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (p.velocity.length() > 0.0) {
+ p.transform.basis.set_axis(1, p.velocity.normalized());
+ } else {
+ p.transform.basis.set_axis(1, p.transform.basis.get_axis(1));
+ }
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+
+ } else {
+ p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0));
+ p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0));
+ p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+ }
+
+ } else {
+ //orient particle Y towards velocity
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (p.velocity.length() > 0.0) {
+ p.transform.basis.set_axis(1, p.velocity.normalized());
+ } else {
+ p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized());
+ }
+ if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) {
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
+ } else {
+ p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
+ p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ }
+ } else {
+ p.transform.basis.orthonormalize();
+ }
+
+ //turn particle by rotation in Y
+ if (flags[FLAG_ROTATE_Y]) {
+ Basis rot_y(Vector3(0, 1, 0), p.custom[0]);
+ p.transform.basis = p.transform.basis * rot_y;
+ }
+ }
+
+ //scale by scale
+ float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
+ if (base_scale == 0.0) base_scale = 0.000001;
+
+ p.transform.basis.scale(Vector3(1, 1, 1) * base_scale);
+
+ if (flags[FLAG_DISABLE_Z]) {
+ p.velocity.z = 0.0;
+ p.transform.origin.z = 0.0;
+ }
+
+ p.transform.origin += p.velocity * local_delta;
+ }
+}
+
+void CPUParticles::_update_particle_data_buffer() {
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+
+ {
+
+ int pc = particles.size();
+
+ PoolVector<int>::Write ow;
+ int *order = NULL;
+
+ PoolVector<float>::Write w = particle_data.write();
+ PoolVector<Particle>::Read r = particles.read();
+ float *ptr = w.ptr();
+
+ Transform un_transform;
+ if (!local_coords) {
+ un_transform = get_global_transform().affine_inverse();
+ }
+
+ if (draw_order != DRAW_ORDER_INDEX) {
+ ow = particle_order.write();
+ order = ow.ptr();
+
+ for (int i = 0; i < pc; i++) {
+ order[i] = i;
+ }
+ if (draw_order == DRAW_ORDER_LIFETIME) {
+ SortArray<int, SortLifetime> sorter;
+ sorter.compare.particles = r.ptr();
+ sorter.sort(order, pc);
+ } else if (draw_order == DRAW_ORDER_VIEW_DEPTH) {
+ Camera *c = get_viewport()->get_camera();
+ if (c) {
+ Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close
+
+ if (local_coords) {
+ dir = un_transform.basis.xform(dir).normalized();
+ }
+
+ SortArray<int, SortAxis> sorter;
+ sorter.compare.particles = r.ptr();
+ sorter.compare.axis = dir;
+ sorter.sort(order, pc);
+ }
+ }
+ }
+
+ for (int i = 0; i < pc; i++) {
+
+ int idx = order ? order[i] : i;
+
+ Transform t = r[idx].transform;
+
+ if (!local_coords) {
+ t = un_transform * t;
+ }
+
+ // print_line(" particle " + itos(i) + ": " + String(r[idx].active ? "[x]" : "[ ]") + "\n\txform " + r[idx].transform + "\n\t" + r[idx].velocity + "\n\tcolor: " + r[idx].color);
+
+ if (r[idx].active) {
+ ptr[0] = t.basis.elements[0][0];
+ ptr[1] = t.basis.elements[0][1];
+ ptr[2] = t.basis.elements[0][2];
+ ptr[3] = t.origin.x;
+ ptr[4] = t.basis.elements[1][0];
+ ptr[5] = t.basis.elements[1][1];
+ ptr[6] = t.basis.elements[1][2];
+ ptr[7] = t.origin.y;
+ ptr[8] = t.basis.elements[2][0];
+ ptr[9] = t.basis.elements[2][1];
+ ptr[10] = t.basis.elements[2][2];
+ ptr[11] = t.origin.z;
+ } else {
+ zeromem(ptr, sizeof(float) * 12);
+ }
+
+ Color c = r[idx].color;
+ uint8_t *data8 = (uint8_t *)&ptr[12];
+ data8[0] = CLAMP(c.r * 255.0, 0, 255);
+ data8[1] = CLAMP(c.g * 255.0, 0, 255);
+ data8[2] = CLAMP(c.b * 255.0, 0, 255);
+ data8[3] = CLAMP(c.a * 255.0, 0, 255);
+
+ ptr[13] = r[idx].custom[0];
+ ptr[14] = r[idx].custom[1];
+ ptr[15] = r[idx].custom[2];
+ ptr[16] = r[idx].custom[3];
+
+ ptr += 17;
+ }
+ }
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+}
+
+void CPUParticles::_update_render_thread() {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+
+ VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+}
+
+void CPUParticles::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (is_processing_internal()) {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, true);
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ if (is_processing_internal()) {
+
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, false);
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ }
+ }
+
+ if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
+ }
+
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+
+ if (particles.size() == 0)
+ return;
+
+ float delta = get_process_delta_time();
+ if (emitting) {
+
+ inactive_time = 0;
+ } else {
+ inactive_time += delta;
+ if (inactive_time > lifetime * 1.2) {
+ set_process_internal(false);
+#ifndef NO_THREADS
+ update_mutex->lock();
+#endif
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE, false);
+
+#ifndef NO_THREADS
+ update_mutex->unlock();
+#endif
+ //reset variables
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
+ }
+ }
+
+ if (time == 0 && pre_process_time > 0.0) {
+
+ float frame_time;
+ if (fixed_fps > 0)
+ frame_time = 1.0 / fixed_fps;
+ else
+ frame_time = 1.0 / 30.0;
+
+ float todo = pre_process_time;
+
+ while (todo >= 0) {
+ _particles_process(frame_time);
+ todo -= frame_time;
+ }
+ }
+
+ if (fixed_fps > 0) {
+ float frame_time = 1.0 / fixed_fps;
+ float decr = frame_time;
+
+ float ldelta = delta;
+ if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
+ ldelta = 0.1;
+ } else if (ldelta <= 0.0) { //unlikely but..
+ ldelta = 0.001;
+ }
+ float todo = frame_remainder + ldelta;
+
+ while (todo >= frame_time) {
+ _particles_process(frame_time);
+ todo -= decr;
+ }
+
+ frame_remainder = todo;
+
+ } else {
+ _particles_process(delta);
+ }
+
+ _update_particle_data_buffer();
+ }
+}
+
+void CPUParticles::convert_from_particles(Node *p_particles) {
+
+ Particles *particles = Object::cast_to<Particles>(p_particles);
+ ERR_FAIL_COND(!particles);
+
+ set_emitting(particles->is_emitting());
+ set_amount(particles->get_amount());
+ set_lifetime(particles->get_lifetime());
+ set_one_shot(particles->get_one_shot());
+ set_pre_process_time(particles->get_pre_process_time());
+ set_explosiveness_ratio(particles->get_explosiveness_ratio());
+ set_randomness_ratio(particles->get_randomness_ratio());
+ set_use_local_coordinates(particles->get_use_local_coordinates());
+ set_fixed_fps(particles->get_fixed_fps());
+ set_fractional_delta(particles->get_fractional_delta());
+ set_speed_scale(particles->get_speed_scale());
+ set_draw_order(DrawOrder(particles->get_draw_order()));
+ set_mesh(particles->get_draw_pass_mesh(0));
+
+ Ref<ParticlesMaterial> material = particles->get_process_material();
+ if (material.is_null())
+ return;
+
+ set_spread(material->get_spread());
+ set_flatness(material->get_flatness());
+
+ set_color(material->get_color());
+
+ Ref<GradientTexture> gt = material->get_color_ramp();
+ if (gt.is_valid()) {
+ set_color_ramp(gt->get_gradient());
+ }
+
+ set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
+ set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
+ set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
+ set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
+
+ set_emission_shape(EmissionShape(material->get_emission_shape()));
+ set_emission_sphere_radius(material->get_emission_sphere_radius());
+ set_emission_box_extents(material->get_emission_box_extents());
+
+ set_gravity(material->get_gravity());
+
+#define CONVERT_PARAM(m_param) \
+ set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \
+ { \
+ Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \
+ if (ctex.is_valid()) set_param_curve(m_param, ctex->get_curve()); \
+ } \
+ set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param));
+
+ CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY);
+ CONVERT_PARAM(PARAM_ANGULAR_VELOCITY);
+ // CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
+ CONVERT_PARAM(PARAM_LINEAR_ACCEL);
+ CONVERT_PARAM(PARAM_RADIAL_ACCEL);
+ CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL);
+ CONVERT_PARAM(PARAM_DAMPING);
+ CONVERT_PARAM(PARAM_ANGLE);
+ CONVERT_PARAM(PARAM_SCALE);
+ CONVERT_PARAM(PARAM_HUE_VARIATION);
+ CONVERT_PARAM(PARAM_ANIM_SPEED);
+ CONVERT_PARAM(PARAM_ANIM_OFFSET);
+
+#undef CONVERT_PARAM
+}
+
+void CPUParticles::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles::set_emitting);
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles::set_amount);
+ ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles::set_lifetime);
+ ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles::set_one_shot);
+ ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles::set_pre_process_time);
+ ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles::set_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles::set_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles::set_fixed_fps);
+ ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles::set_fractional_delta);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles::set_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles::is_emitting);
+ ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles::get_amount);
+ ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles::get_lifetime);
+ ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles::get_one_shot);
+ ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles::get_pre_process_time);
+ ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles::get_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles::get_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles::get_fixed_fps);
+ ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles::get_fractional_delta);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &CPUParticles::get_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &CPUParticles::set_draw_order);
+
+ ClassDB::bind_method(D_METHOD("get_draw_order"), &CPUParticles::get_draw_order);
+
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CPUParticles::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("restart"), &CPUParticles::restart);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
+ ADD_GROUP("Time", "");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
+ ADD_GROUP("Drawing", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+
+ BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
+ BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
+ BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH);
+
+ ////////////////////////////////
+
+ ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &CPUParticles::set_spread);
+ ClassDB::bind_method(D_METHOD("get_spread"), &CPUParticles::get_spread);
+
+ ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles::set_flatness);
+ ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles::get_flatness);
+
+ ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles::set_param);
+ ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles::get_param);
+
+ ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles::set_param_randomness);
+ ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles::get_param_randomness);
+
+ ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles::set_param_curve);
+ ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles::get_param_curve);
+
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &CPUParticles::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &CPUParticles::get_color);
+
+ ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles::set_color_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles::get_color_ramp);
+
+ ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles::set_particle_flag);
+ ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles::get_particle_flag);
+
+ ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles::set_emission_shape);
+ ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles::get_emission_shape);
+
+ ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &CPUParticles::set_emission_sphere_radius);
+ ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &CPUParticles::get_emission_sphere_radius);
+
+ ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &CPUParticles::set_emission_box_extents);
+ ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &CPUParticles::get_emission_box_extents);
+
+ ClassDB::bind_method(D_METHOD("set_emission_points", "array"), &CPUParticles::set_emission_points);
+ ClassDB::bind_method(D_METHOD("get_emission_points"), &CPUParticles::get_emission_points);
+
+ ClassDB::bind_method(D_METHOD("set_emission_normals", "array"), &CPUParticles::set_emission_normals);
+ ClassDB::bind_method(D_METHOD("get_emission_normals"), &CPUParticles::get_emission_normals);
+
+ ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles::set_emission_colors);
+ ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles::get_emission_colors);
+
+ ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity);
+
+ ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles::convert_from_particles);
+
+ ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread);
+
+ ADD_GROUP("Emission Shape", "emission_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
+ ADD_GROUP("Flags", "flag_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_particle_flag", "get_particle_flag", FLAG_DISABLE_Z);
+ ADD_GROUP("Spread", "");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
+ ADD_GROUP("Gravity", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("Initial Velocity", "initial_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_GROUP("Angular Velocity", "angular_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
+ /*
+ ADD_GROUP("Orbit Velocity", "orbit_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
+*/
+ ADD_GROUP("Linear Accel", "linear_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL);
+ ADD_GROUP("Radial Accel", "radial_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL);
+ ADD_GROUP("Tangential Accel", "tangential_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL);
+ ADD_GROUP("Damping", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING);
+ ADD_GROUP("Angle", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE);
+ ADD_GROUP("Scale", "");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE);
+ ADD_GROUP("Color", "");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
+
+ ADD_GROUP("Hue Variation", "hue_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION);
+ ADD_GROUP("Animation", "anim_");
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
+
+ BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
+ //BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL);
+ BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL);
+ BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL);
+ BIND_ENUM_CONSTANT(PARAM_DAMPING);
+ BIND_ENUM_CONSTANT(PARAM_ANGLE);
+ BIND_ENUM_CONSTANT(PARAM_SCALE);
+ BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION);
+ BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED);
+ BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET);
+ BIND_ENUM_CONSTANT(PARAM_MAX);
+
+ BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(FLAG_MAX);
+
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
+}
+
+CPUParticles::CPUParticles() {
+
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+
+ multimesh = VisualServer::get_singleton()->multimesh_create();
+ set_base(multimesh);
+
+ set_emitting(true);
+ set_one_shot(false);
+ set_amount(8);
+ set_lifetime(1);
+ set_fixed_fps(0);
+ set_fractional_delta(true);
+ set_pre_process_time(0);
+ set_explosiveness_ratio(0);
+ set_randomness_ratio(0);
+ set_use_local_coordinates(true);
+
+ set_draw_order(DRAW_ORDER_INDEX);
+ set_speed_scale(1);
+
+ set_spread(45);
+ set_flatness(0);
+ set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
+ //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_LINEAR_ACCEL, 0);
+ set_param(PARAM_RADIAL_ACCEL, 0);
+ set_param(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param(PARAM_DAMPING, 0);
+ set_param(PARAM_ANGLE, 0);
+ set_param(PARAM_SCALE, 1);
+ set_param(PARAM_HUE_VARIATION, 0);
+ set_param(PARAM_ANIM_SPEED, 0);
+ set_param(PARAM_ANIM_OFFSET, 0);
+ set_emission_shape(EMISSION_SHAPE_POINT);
+ set_emission_sphere_radius(1);
+ set_emission_box_extents(Vector3(1, 1, 1));
+
+ set_gravity(Vector3(0, -9.8, 0));
+
+ for (int i = 0; i < PARAM_MAX; i++) {
+ set_param_randomness(Parameter(i), 0);
+ }
+
+ for (int i = 0; i < FLAG_MAX; i++) {
+ flags[i] = false;
+ }
+
+ set_color(Color(1, 1, 1, 1));
+
+#ifndef NO_THREADS
+ update_mutex = Mutex::create();
+#endif
+}
+
+CPUParticles::~CPUParticles() {
+ VS::get_singleton()->free(multimesh);
+
+#ifndef NO_THREADS
+ memdelete(update_mutex);
+#endif
+}
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
new file mode 100644
index 0000000000..1ee709719d
--- /dev/null
+++ b/scene/3d/cpu_particles.h
@@ -0,0 +1,258 @@
+#ifndef CPU_PARTICLES_H
+#define CPU_PARTICLES_H
+#include "rid.h"
+#include "scene/3d/visual_instance.h"
+#include "scene/main/timer.h"
+#include "scene/resources/material.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class CPUParticles : public GeometryInstance {
+private:
+ GDCLASS(CPUParticles, GeometryInstance);
+
+public:
+ enum DrawOrder {
+ DRAW_ORDER_INDEX,
+ DRAW_ORDER_LIFETIME,
+ DRAW_ORDER_VIEW_DEPTH,
+ };
+
+ enum Parameter {
+
+ PARAM_INITIAL_LINEAR_VELOCITY,
+ PARAM_ANGULAR_VELOCITY,
+ //PARAM_ORBIT_VELOCITY,
+ PARAM_LINEAR_ACCEL,
+ PARAM_RADIAL_ACCEL,
+ PARAM_TANGENTIAL_ACCEL,
+ PARAM_DAMPING,
+ PARAM_ANGLE,
+ PARAM_SCALE,
+ PARAM_HUE_VARIATION,
+ PARAM_ANIM_SPEED,
+ PARAM_ANIM_OFFSET,
+ PARAM_MAX
+ };
+
+ enum Flags {
+ FLAG_ALIGN_Y_TO_VELOCITY,
+ FLAG_ROTATE_Y,
+ FLAG_DISABLE_Z,
+ FLAG_ANIM_LOOP,
+ FLAG_MAX
+ };
+
+ enum EmissionShape {
+ EMISSION_SHAPE_POINT,
+ EMISSION_SHAPE_SPHERE,
+ EMISSION_SHAPE_BOX,
+ EMISSION_SHAPE_POINTS,
+ EMISSION_SHAPE_DIRECTED_POINTS,
+ };
+
+private:
+ bool emitting;
+
+ struct Particle {
+ Transform transform;
+ Color color;
+ float custom[4];
+ Vector3 velocity;
+ bool active;
+ float angle_rand;
+ float scale_rand;
+ float hue_rot_rand;
+ float anim_offset_rand;
+ float time;
+ Color base_color;
+
+ uint32_t seed;
+ };
+
+ float time;
+ float inactive_time;
+ float frame_remainder;
+ int cycle;
+
+ RID multimesh;
+
+ PoolVector<Particle> particles;
+ PoolVector<float> particle_data;
+ PoolVector<int> particle_order;
+
+ struct SortLifetime {
+ const Particle *particles;
+
+ bool operator()(int p_a, int p_b) const {
+ return particles[p_a].time < particles[p_b].time;
+ }
+ };
+
+ struct SortAxis {
+ const Particle *particles;
+ Vector3 axis;
+ bool operator()(int p_a, int p_b) const {
+
+ return axis.dot(particles[p_a].transform.origin) < axis.dot(particles[p_b].transform.origin);
+ }
+ };
+
+ //
+
+ bool one_shot;
+
+ float lifetime;
+ float pre_process_time;
+ float explosiveness_ratio;
+ float randomness_ratio;
+ float speed_scale;
+ bool local_coords;
+ int fixed_fps;
+ bool fractional_delta;
+
+ DrawOrder draw_order;
+
+ Ref<Mesh> mesh;
+
+ ////////
+
+ float spread;
+ float flatness;
+
+ float parameters[PARAM_MAX];
+ float randomness[PARAM_MAX];
+
+ Ref<Curve> curve_parameters[PARAM_MAX];
+ Color color;
+ Ref<Gradient> color_ramp;
+
+ bool flags[FLAG_MAX];
+
+ EmissionShape emission_shape;
+ float emission_sphere_radius;
+ Vector3 emission_box_extents;
+ PoolVector<Vector3> emission_points;
+ PoolVector<Vector3> emission_normals;
+ PoolVector<Color> emission_colors;
+ int emission_point_count;
+
+ bool anim_loop;
+ Vector3 gravity;
+
+ void _particles_process(float p_delta);
+ void _update_particle_data_buffer();
+
+ Mutex *update_mutex;
+
+ void _update_render_thread();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+ virtual void _validate_property(PropertyInfo &property) const;
+
+public:
+ AABB get_aabb() const;
+ PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_emitting(bool p_emitting);
+ void set_amount(int p_amount);
+ void set_lifetime(float p_lifetime);
+ void set_one_shot(bool p_one_shot);
+ void set_pre_process_time(float p_time);
+ void set_explosiveness_ratio(float p_ratio);
+ void set_randomness_ratio(float p_ratio);
+ void set_visibility_aabb(const AABB &p_aabb);
+ void set_use_local_coordinates(bool p_enable);
+ void set_speed_scale(float p_scale);
+
+ bool is_emitting() const;
+ int get_amount() const;
+ float get_lifetime() const;
+ bool get_one_shot() const;
+ float get_pre_process_time() const;
+ float get_explosiveness_ratio() const;
+ float get_randomness_ratio() const;
+ AABB get_visibility_aabb() const;
+ bool get_use_local_coordinates() const;
+ float get_speed_scale() const;
+
+ void set_fixed_fps(int p_count);
+ int get_fixed_fps() const;
+
+ void set_fractional_delta(bool p_enable);
+ bool get_fractional_delta() const;
+
+ void set_draw_order(DrawOrder p_order);
+ DrawOrder get_draw_order() const;
+
+ void set_draw_passes(int p_count);
+ int get_draw_passes() const;
+
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ ///////////////////
+
+ void set_spread(float p_spread);
+ float get_spread() const;
+
+ void set_flatness(float p_flatness);
+ float get_flatness() const;
+
+ void set_param(Parameter p_param, float p_value);
+ float get_param(Parameter p_param) const;
+
+ void set_param_randomness(Parameter p_param, float p_value);
+ float get_param_randomness(Parameter p_param) const;
+
+ void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve);
+ Ref<Curve> get_param_curve(Parameter p_param) const;
+
+ void set_color(const Color &p_color);
+ Color get_color() const;
+
+ void set_color_ramp(const Ref<Gradient> &p_texture);
+ Ref<Gradient> get_color_ramp() const;
+
+ void set_particle_flag(Flags p_flag, bool p_enable);
+ bool get_particle_flag(Flags p_flag) const;
+
+ void set_emission_shape(EmissionShape p_shape);
+ void set_emission_sphere_radius(float p_radius);
+ void set_emission_box_extents(Vector3 p_extents);
+ void set_emission_points(const PoolVector<Vector3> &p_points);
+ void set_emission_normals(const PoolVector<Vector3> &p_normals);
+ void set_emission_colors(const PoolVector<Color> &p_colors);
+ void set_emission_point_count(int p_count);
+
+ EmissionShape get_emission_shape() const;
+ float get_emission_sphere_radius() const;
+ Vector3 get_emission_box_extents() const;
+ PoolVector<Vector3> get_emission_points() const;
+ PoolVector<Vector3> get_emission_normals() const;
+ PoolVector<Color> get_emission_colors() const;
+ int get_emission_point_count() const;
+
+ void set_gravity(const Vector3 &p_gravity);
+ Vector3 get_gravity() const;
+
+ virtual String get_configuration_warning() const;
+
+ void restart();
+
+ void convert_from_particles(Node *p_particles);
+
+ CPUParticles();
+ ~CPUParticles();
+};
+
+VARIANT_ENUM_CAST(CPUParticles::DrawOrder)
+VARIANT_ENUM_CAST(CPUParticles::Parameter)
+VARIANT_ENUM_CAST(CPUParticles::Flags)
+VARIANT_ENUM_CAST(CPUParticles::EmissionShape)
+
+#endif // CPU_PARTICLES_H
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 4ad2eb60ee..6276d02eff 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -557,6 +557,7 @@ GIProbe::GIProbe() {
compress = false;
gi_probe = VS::get_singleton()->gi_probe_create();
+ set_disable_scale(true);
}
GIProbe::~GIProbe() {
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 7c42638107..16164cf3bf 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -306,6 +306,7 @@ Light::Light(VisualServer::LightType p_type) {
set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5);
set_param(PARAM_SHADOW_NORMAL_BIAS, 0.0);
set_param(PARAM_SHADOW_BIAS, 0.15);
+ set_disable_scale(true);
}
Light::Light() {
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 80bae911d4..722eadb9ca 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -36,6 +36,7 @@
#include "scene/resources/material.h"
#include "scene/scene_string_names.h"
#include "skeleton.h"
+
bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) {
//this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else.
@@ -371,7 +372,7 @@ void MeshInstance::_bind_methods() {
ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton_path", "get_skeleton_path");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
}
MeshInstance::MeshInstance() {
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 5d359cd4d5..0dfec538f9 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance {
GDCLASS(MeshInstance, GeometryInstance);
+protected:
Ref<Mesh> mesh;
NodePath skeleton_path;
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 5056fb2fe4..7529a0e524 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -30,6 +30,7 @@
#include "physics_body.h"
+#include "core/core_string_names.h"
#include "engine.h"
#include "method_bind_ext.gen.inc"
#include "scene/scene_string_names.h"
@@ -121,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const {
void PhysicsBody::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::remove_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::_set_layers(uint32_t p_mask) {
@@ -178,28 +179,75 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) :
collision_mask = 1;
}
+#ifndef DISABLE_DEPRECATED
void StaticBody::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
+
real_t StaticBody::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void StaticBody::set_bounce(real_t p_bounce) {
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
+
real_t StaticBody::get_bounce() const {
- return bounce;
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
+
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void StaticBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> StaticBody::get_physics_material_override() const {
+ return physics_material_override;
}
void StaticBody::set_constant_linear_velocity(const Vector3 &p_vel) {
@@ -236,24 +284,39 @@ void StaticBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
-
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
}
StaticBody::StaticBody() :
PhysicsBody(PhysicsServer::BODY_MODE_STATIC) {
-
- bounce = 0;
- friction = 1;
}
-StaticBody::~StaticBody() {
+StaticBody::~StaticBody() {}
+
+void StaticBody::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT);
+ } else {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode());
+ }
}
void RigidBody::_body_enter_tree(ObjectID p_id) {
@@ -550,28 +613,67 @@ real_t RigidBody::get_weight() const {
return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8));
}
+#ifndef DISABLE_DEPRECATED
void RigidBody::set_friction(real_t p_friction) {
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
- friction = p_friction;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_friction(p_friction);
+ _reload_physics_characteristics();
}
real_t RigidBody::get_friction() const {
- return friction;
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+ if (physics_material_override.is_null()) {
+ return 1;
+ }
+
+ return physics_material_override->get_friction();
}
void RigidBody::set_bounce(real_t p_bounce) {
-
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
- bounce = p_bounce;
- PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+ if (physics_material_override.is_null()) {
+ physics_material_override.instance();
+ }
+ physics_material_override->set_bounce(p_bounce);
+ _reload_physics_characteristics();
}
real_t RigidBody::get_bounce() const {
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material")
+ WARN_DEPRECATED
+ if (physics_material_override.is_null()) {
+ return 0;
+ }
- return bounce;
+ return physics_material_override->get_bounce();
+}
+#endif
+
+void RigidBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
+ if (physics_material_override.is_valid()) {
+ physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+
+ physics_material_override = p_physics_material_override;
+
+ if (physics_material_override.is_valid()) {
+ physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics");
+ }
+ _reload_physics_characteristics();
+}
+
+Ref<PhysicsMaterial> RigidBody::get_physics_material_override() const {
+ return physics_material_override;
}
void RigidBody::set_gravity_scale(real_t p_gravity_scale) {
@@ -812,6 +914,11 @@ void RigidBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody::set_bounce);
ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody::get_bounce);
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody::get_physics_material_override);
+
+ ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody::_reload_physics_characteristics);
+
ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody::set_linear_velocity);
ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody::get_linear_velocity);
@@ -865,6 +972,7 @@ void RigidBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection");
@@ -903,9 +1011,7 @@ RigidBody::RigidBody() :
mode = MODE_RIGID;
- bounce = 0;
mass = 1;
- friction = 1;
max_contacts_reported = 0;
state = NULL;
@@ -929,6 +1035,21 @@ RigidBody::~RigidBody() {
if (contact_monitor)
memdelete(contact_monitor);
}
+
+void RigidBody::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT);
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT);
+ } else {
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce());
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode());
+ PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode());
+ }
+}
+
//////////////////////////////////////////////////////
//////////////////////////
@@ -979,7 +1100,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in
return colliding;
}
-Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
+Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
Vector3 lv = p_linear_velocity;
@@ -1128,7 +1249,7 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) {
void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_slides", "floor_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move);
@@ -2228,6 +2349,7 @@ void PhysicalBone::set_bounce(real_t p_bounce) {
bounce = p_bounce;
PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
}
+
real_t PhysicalBone::get_bounce() const {
return bounce;
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 17d2769c79..44d6502be1 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -32,6 +32,7 @@
#define PHYSICS_BODY__H
#include "scene/3d/collision_object.h"
+#include "scene/resources/physics_material.h"
#include "servers/physics_server.h"
#include "skeleton.h"
#include "vset.h"
@@ -81,18 +82,22 @@ class StaticBody : public PhysicsBody {
Vector3 constant_linear_velocity;
Vector3 constant_angular_velocity;
- real_t bounce;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
protected:
static void _bind_methods();
public:
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_constant_linear_velocity(const Vector3 &p_vel);
void set_constant_angular_velocity(const Vector3 &p_vel);
@@ -102,6 +107,9 @@ public:
StaticBody();
~StaticBody();
+
+private:
+ void _reload_physics_characteristics();
};
class RigidBody : public PhysicsBody {
@@ -121,9 +129,8 @@ protected:
PhysicsDirectBodyState *state;
Mode mode;
- real_t bounce;
real_t mass;
- real_t friction;
+ Ref<PhysicsMaterial> physics_material_override;
Vector3 linear_velocity;
Vector3 angular_velocity;
@@ -196,11 +203,16 @@ public:
void set_weight(real_t p_weight);
real_t get_weight() const;
+#ifndef DISABLE_DEPRECATED
void set_friction(real_t p_friction);
real_t get_friction() const;
void set_bounce(real_t p_bounce);
real_t get_bounce() const;
+#endif
+
+ void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
+ Ref<PhysicsMaterial> get_physics_material_override() const;
void set_linear_velocity(const Vector3 &p_velocity);
Vector3 get_linear_velocity() const;
@@ -249,6 +261,9 @@ public:
RigidBody();
~RigidBody();
+
+private:
+ void _reload_physics_characteristics();
};
VARIANT_ENUM_CAST(RigidBody::Mode);
@@ -294,7 +309,7 @@ protected:
static void _bind_methods();
public:
- bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision);
+ bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz);
bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia);
void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock);
@@ -303,7 +318,7 @@ public:
void set_safe_margin(float p_margin);
float get_safe_margin() const;
- Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
index b2d10006f7..7988c43eab 100644
--- a/scene/3d/physics_joint.cpp
+++ b/scene/3d/physics_joint.cpp
@@ -154,8 +154,8 @@ void Joint::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
@@ -260,7 +260,7 @@ void HingeJoint::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_lower_limit", "lower_limit"), &HingeJoint::_set_lower_limit);
ClassDB::bind_method(D_METHOD("_get_lower_limit"), &HingeJoint::_get_lower_limit);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"), "set_param", "get_param", PARAM_BIAS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.00,0.99,0.01"), "set_param", "get_param", PARAM_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit/enable"), "set_flag", "get_flag", FLAG_USE_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit/upper", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_upper_limit", "_get_upper_limit");
@@ -270,7 +270,7 @@ void HingeJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE);
BIND_ENUM_CONSTANT(PARAM_BIAS);
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 4d50945062..fe522bbe97 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -274,6 +274,7 @@ ReflectionProbe::ReflectionProbe() {
probe = VisualServer::get_singleton()->reflection_probe_create();
VS::get_singleton()->instance_set_base(get_instance(), probe);
+ set_disable_scale(true);
}
ReflectionProbe::~ReflectionProbe() {
diff --git a/scene/3d/remote_transform.cpp b/scene/3d/remote_transform.cpp
index afb85f7314..2156e24cd0 100644
--- a/scene/3d/remote_transform.cpp
+++ b/scene/3d/remote_transform.cpp
@@ -194,7 +194,7 @@ void RemoteTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_scale", "update_remote_scale"), &RemoteTransform::set_update_scale);
ClassDB::bind_method(D_METHOD("get_update_scale"), &RemoteTransform::get_update_scale);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path"), "set_remote_node", "get_remote_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Spatial"), "set_remote_node", "get_remote_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_coordinates"), "set_use_global_coordinates", "get_use_global_coordinates");
ADD_GROUP("Update", "update_");
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index 76d90dc6ff..8d91b6f09f 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -547,6 +547,8 @@ void Skeleton::localize_rests() {
}
}
+#ifndef _3D_DISABLED
+
void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
ERR_FAIL_INDEX(p_bone, bones.size());
ERR_FAIL_COND(bones[p_bone].physical_bone);
@@ -691,6 +693,8 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(false, this, p_exception);
}
+#endif // _3D_DISABLED
+
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -727,11 +731,15 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
+#ifndef _3D_DISABLED
+
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception);
+#endif // _3D_DISABLED
+
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index dad11960a5..9672acb57a 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -38,7 +38,10 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
+#ifndef _3D_DISABLED
class PhysicalBone;
+#endif // _3D_DISABLED
+
class Skeleton : public Spatial {
GDCLASS(Skeleton, Spatial);
@@ -64,8 +67,10 @@ class Skeleton : public Spatial {
Transform transform_final;
+#ifndef _3D_DISABLED
PhysicalBone *physical_bone;
PhysicalBone *cache_parent_physical_bone;
+#endif // _3D_DISABLED
List<uint32_t> nodes_bound;
@@ -75,8 +80,10 @@ class Skeleton : public Spatial {
ignore_animation = false;
custom_pose_enable = false;
disable_rest = false;
+#ifndef _3D_DISABLED
physical_bone = NULL;
cache_parent_physical_bone = NULL;
+#endif // _3D_DISABLED
}
};
@@ -164,6 +171,7 @@ public:
void localize_rests(); // used for loaders and tools
+#ifndef _3D_DISABLED
// Physical bone API
void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone);
@@ -182,6 +190,7 @@ public:
void physical_bones_start_simulation_on(const Array &p_bones);
void physical_bones_add_collision_exception(RID p_exception);
void physical_bones_remove_collision_exception(RID p_exception);
+#endif // _3D_DISABLED
public:
Skeleton();
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
new file mode 100644
index 0000000000..8498dc34c0
--- /dev/null
+++ b/scene/3d/soft_body.cpp
@@ -0,0 +1,740 @@
+/*************************************************************************/
+/* soft_physics_body.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "soft_body.h"
+#include "os/os.h"
+#include "scene/3d/collision_object.h"
+#include "scene/3d/skeleton.h"
+#include "servers/physics_server.h"
+
+SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {}
+
+void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) {
+ clear();
+
+ ERR_FAIL_COND(!p_mesh.is_valid());
+
+ mesh = p_mesh;
+ surface = p_surface;
+
+ const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface);
+ const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface);
+ const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface);
+ uint32_t surface_offsets[VS::ARRAY_MAX];
+
+ buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface);
+ stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets);
+ offset_vertices = surface_offsets[VS::ARRAY_VERTEX];
+ offset_normal = surface_offsets[VS::ARRAY_NORMAL];
+}
+
+void SoftBodyVisualServerHandler::clear() {
+
+ if (mesh.is_valid()) {
+ buffer.resize(0);
+ }
+
+ mesh = RID();
+}
+
+void SoftBodyVisualServerHandler::open() {
+ write_buffer = buffer.write();
+}
+
+void SoftBodyVisualServerHandler::close() {
+ write_buffer = PoolVector<uint8_t>::Write();
+}
+
+void SoftBodyVisualServerHandler::commit_changes() {
+ VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer);
+}
+
+void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) {
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb);
+}
+
+SoftBody::PinnedPoint::PinnedPoint() :
+ point_index(-1),
+ spatial_attachment(NULL) {
+}
+
+SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
+ point_index = obj_tocopy.point_index;
+ spatial_attachment_path = obj_tocopy.spatial_attachment_path;
+ spatial_attachment = obj_tocopy.spatial_attachment;
+ vertex_offset_transform = obj_tocopy.vertex_offset_transform;
+}
+
+void SoftBody::_update_pickable() {
+ if (!is_inside_tree())
+ return;
+ bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree();
+ PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable);
+}
+
+bool SoftBody::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+
+ return _set_property_pinned_points_indices(p_value);
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _set_property_pinned_points_attachment(idx, what, p_value);
+ }
+
+ return false;
+}
+
+bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+ Array arr_ret;
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ arr_ret.resize(pinned_points_indices_size);
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ arr_ret[i] = r[i].point_index;
+ }
+
+ r_ret = arr_ret;
+ return true;
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _get_property_pinned_points(idx, what, r_ret);
+ }
+
+ return false;
+}
+
+void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ const int pinned_points_indices_size = pinned_points_indices.size();
+
+ p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points"));
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index"));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path"));
+ }
+}
+
+bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) {
+
+ const int p_indices_size = p_indices.size();
+
+ { // Remove the pined points on physics server that will be removed by resize
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ if (p_indices_size < pinned_points_indices.size()) {
+ for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) {
+ pin_point(r[i].point_index, false);
+ }
+ }
+ }
+
+ pinned_points_indices.resize(p_indices_size);
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ int point_index;
+ for (int i = 0; i < p_indices_size; ++i) {
+ point_index = p_indices.get(i);
+ if (w[i].point_index != point_index) {
+ if (-1 != w[i].point_index)
+ pin_point(w[i].point_index, false);
+ w[i].point_index = point_index;
+ pin_point(w[i].point_index, true);
+ }
+ }
+ return true;
+}
+
+bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+
+ if ("spatial_attachment_path" == p_what) {
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ pin_point(w[p_item].point_index, true, p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+
+ if ("point_index" == p_what) {
+ r_ret = r[p_item].point_index;
+ } else if ("spatial_attachment_path" == p_what) {
+ r_ret = r[p_item].spatial_attachment_path;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) {
+#ifdef TOOLS_ENABLED
+ if (p_changed == this) {
+ update_configuration_warning();
+ }
+#endif
+}
+
+void SoftBody::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_WORLD: {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ add_change_receptor(this);
+
+ RID space = get_world()->get_space();
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space);
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+ update_physics_server();
+ } break;
+ case NOTIFICATION_READY: {
+ if (!parent_collision_ignore.is_empty())
+ add_collision_exception_with(get_node(parent_collision_ignore));
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (!simulation_started) {
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+
+ _update_cache_pin_points_datas();
+ // Submit bone attachment
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ if (!r[i].spatial_attachment) {
+ // Use soft body position to update the point position
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin);
+ } else {
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin);
+ }
+ }
+ }
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+
+ _update_pickable();
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID());
+
+ } break;
+ }
+
+#ifdef TOOLS_ENABLED
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ update_configuration_warning();
+ }
+ }
+
+#endif
+}
+
+void SoftBody::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit);
+
+ ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore);
+ ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore);
+
+ ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with);
+ ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with);
+
+ ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision);
+ ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision);
+
+ ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass);
+ ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass);
+
+ ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness);
+ ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness);
+ ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness);
+ ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient);
+ ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient);
+ ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable);
+ ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable);
+
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient");
+}
+
+String SoftBody::get_configuration_warning() const {
+
+ String warning = MeshInstance::get_configuration_warning();
+
+ if (get_mesh().is_null()) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("This body will be ignored until you set a mesh");
+ }
+
+ Transform t = get_transform();
+ if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead.");
+ }
+
+ return warning;
+}
+
+void SoftBody::_draw_soft_mesh() {
+ if (get_mesh().is_null())
+ return;
+
+ if (!visual_server_handler.is_ready()) {
+
+ visual_server_handler.prepare(get_mesh()->get_rid(), 0);
+
+ /// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
+ simulation_started = true;
+ call_deferred("set_as_toplevel", true);
+ call_deferred("set_transform", Transform());
+ }
+
+ visual_server_handler.open();
+ PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler);
+ visual_server_handler.close();
+
+ visual_server_handler.commit_changes();
+}
+
+void SoftBody::update_physics_server() {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ return;
+
+ if (get_mesh().is_valid()) {
+
+ become_mesh_owner();
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
+ VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh");
+ } else {
+
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL);
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh");
+ }
+}
+
+void SoftBody::become_mesh_owner() {
+ if (mesh.is_null())
+ return;
+
+ if (!mesh_owner) {
+ mesh_owner = true;
+
+ ERR_FAIL_COND(!mesh->get_surface_count());
+
+ // Get current mesh array and create new mesh array with necessary flag for softbody
+ Array surface_arrays = mesh->surface_get_arrays(0);
+ Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
+ uint32_t surface_format = mesh->surface_get_format(0);
+
+ surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL);
+ surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
+
+ Ref<ArrayMesh> soft_mesh;
+ soft_mesh.instance();
+ soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format);
+
+ set_mesh(soft_mesh);
+
+ Vector<Ref<Material> > copy_materials;
+ copy_materials.append_array(materials);
+ for (int i = copy_materials.size() - 1; 0 <= i; --i) {
+ set_surface_material(i, copy_materials[i]);
+ }
+ }
+}
+
+void SoftBody::set_collision_mask(uint32_t p_mask) {
+ collision_mask = p_mask;
+ PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask);
+}
+
+uint32_t SoftBody::get_collision_mask() const {
+ return collision_mask;
+}
+void SoftBody::set_collision_layer(uint32_t p_layer) {
+ collision_layer = p_layer;
+ PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer);
+}
+
+uint32_t SoftBody::get_collision_layer() const {
+ return collision_layer;
+}
+
+void SoftBody::set_collision_mask_bit(int p_bit, bool p_value) {
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_mask(mask);
+}
+
+bool SoftBody::get_collision_mask_bit(int p_bit) const {
+ return get_collision_mask() & (1 << p_bit);
+}
+
+void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) {
+ uint32_t layer = get_collision_layer();
+ if (p_value)
+ layer |= 1 << p_bit;
+ else
+ layer &= ~(1 << p_bit);
+ set_collision_layer(layer);
+}
+
+bool SoftBody::get_collision_layer_bit(int p_bit) const {
+ return get_collision_layer() & (1 << p_bit);
+}
+
+void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) {
+ parent_collision_ignore = p_parent_collision_ignore;
+}
+
+const NodePath &SoftBody::get_parent_collision_ignore() const {
+ return parent_collision_ignore;
+}
+
+void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) {
+ pinned_points_indices = p_pinned_points_indices;
+ PoolVector<PinnedPoint>::Read w = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ pin_point(p_pinned_points_indices[i].point_index, true);
+ }
+}
+
+PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() {
+ return pinned_points_indices;
+}
+
+void SoftBody::add_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+void SoftBody::remove_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+int SoftBody::get_simulation_precision() {
+ return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid);
+}
+
+void SoftBody::set_simulation_precision(int p_simulation_precision) {
+ PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision);
+}
+
+real_t SoftBody::get_total_mass() {
+ return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid);
+}
+
+void SoftBody::set_total_mass(real_t p_total_mass) {
+ PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass);
+}
+
+void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness);
+}
+
+real_t SoftBody::get_linear_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid);
+}
+
+void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness);
+}
+
+real_t SoftBody::get_areaAngular_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid);
+}
+
+void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness);
+}
+
+real_t SoftBody::get_volume_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid);
+}
+
+real_t SoftBody::get_pressure_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid);
+}
+
+void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient);
+}
+
+real_t SoftBody::get_pose_matching_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid);
+}
+
+void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient);
+}
+
+real_t SoftBody::get_damping_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid);
+}
+
+void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient);
+}
+
+real_t SoftBody::get_drag_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid);
+}
+
+void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient);
+}
+
+Vector3 SoftBody::get_point_transform(int p_point_index) {
+ return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index);
+}
+
+void SoftBody::pin_point_toggle(int p_point_index) {
+ pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index)));
+}
+
+void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) {
+ _pin_point_on_physics_server(p_point_index, pin);
+ if (pin) {
+ _add_pinned_point(p_point_index, p_spatial_attachment_path);
+ } else {
+ _remove_pinned_point(p_point_index);
+ }
+}
+
+bool SoftBody::is_point_pinned(int p_point_index) const {
+ return -1 != _has_pinned_point(p_point_index);
+}
+
+void SoftBody::set_ray_pickable(bool p_ray_pickable) {
+
+ ray_pickable = p_ray_pickable;
+ _update_pickable();
+}
+
+bool SoftBody::is_ray_pickable() const {
+
+ return ray_pickable;
+}
+
+SoftBody::SoftBody() :
+ MeshInstance(),
+ physics_rid(PhysicsServer::get_singleton()->soft_body_create()),
+ mesh_owner(false),
+ collision_mask(1),
+ collision_layer(1),
+ simulation_started(false),
+ pinned_points_cache_dirty(true) {
+
+ PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
+}
+
+SoftBody::~SoftBody() {
+}
+
+void SoftBody::reset_softbody_pin() {
+ PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid);
+ PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 < i; --i) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true);
+ }
+}
+
+void SoftBody::_update_cache_pin_points_datas() {
+ if (pinned_points_cache_dirty) {
+ pinned_points_cache_dirty = false;
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+
+ if (!w[i].spatial_attachment_path.is_empty()) {
+ w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path));
+ if (w[i].spatial_attachment) {
+
+ Transform point_global_transform(get_global_transform());
+ point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index));
+
+ // Local transform relative to spatial attachment node
+ w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform;
+ continue;
+ } else {
+ ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node.");
+ }
+ }
+ // Local transform relative to Soft body
+ w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index);
+ w[i].vertex_offset_transform.basis = Basis();
+ }
+ }
+}
+
+void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin);
+}
+
+void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) {
+ SoftBody::PinnedPoint *pinned_point;
+ if (-1 == _get_pinned_point(p_point_index, pinned_point)) {
+ // Create new
+ PinnedPoint pp;
+ pp.point_index = p_point_index;
+ pp.spatial_attachment_path = p_spatial_attachment_path;
+ pinned_points_indices.push_back(pp);
+ } else {
+ // Update
+ pinned_point->point_index = p_point_index;
+ pinned_point->spatial_attachment_path = p_spatial_attachment_path;
+ }
+}
+
+void SoftBody::_remove_pinned_point(int p_point_index) {
+ const int id(_has_pinned_point(p_point_index));
+ if (-1 != id) {
+ pinned_points_indices.remove(id);
+ }
+}
+
+int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const {
+ const int id = _has_pinned_point(p_point_index);
+ if (-1 == id) {
+ r_point = NULL;
+ return -1;
+ } else {
+ r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]);
+ return id;
+ }
+}
+
+int SoftBody::_has_pinned_point(int p_point_index) const {
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ if (p_point_index == r[i].point_index) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h
new file mode 100644
index 0000000000..f44f337698
--- /dev/null
+++ b/scene/3d/soft_body.h
@@ -0,0 +1,197 @@
+/*************************************************************************/
+/* soft_physics_body.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SOFT_PHYSICS_BODY_H
+#define SOFT_PHYSICS_BODY_H
+
+#include "scene/3d/mesh_instance.h"
+
+class SoftBody;
+
+class SoftBodyVisualServerHandler {
+
+ friend class SoftBody;
+
+ RID mesh;
+ int surface;
+ PoolVector<uint8_t> buffer;
+ uint32_t stride;
+ uint32_t offset_vertices;
+ uint32_t offset_normal;
+
+ PoolVector<uint8_t>::Write write_buffer;
+
+private:
+ SoftBodyVisualServerHandler();
+ bool is_ready() { return mesh.is_valid(); }
+ void prepare(RID p_mesh_rid, int p_surface);
+ void clear();
+ void open();
+ void close();
+ void commit_changes();
+
+public:
+ void set_vertex(int p_vertex_id, const void *p_vector3);
+ void set_normal(int p_vertex_id, const void *p_vector3);
+ void set_aabb(const AABB &p_aabb);
+};
+
+class SoftBody : public MeshInstance {
+ GDCLASS(SoftBody, MeshInstance);
+
+public:
+ struct PinnedPoint {
+ int point_index;
+ NodePath spatial_attachment_path;
+ Spatial *spatial_attachment; // Cache
+ /// This is the offset from the soft body to point or attachment to point
+ /// Depend if the spatial_attachment_node is NULL or not
+ Transform vertex_offset_transform; // Cache
+
+ PinnedPoint();
+ PinnedPoint(const PinnedPoint &obj_tocopy);
+ };
+
+private:
+ SoftBodyVisualServerHandler visual_server_handler;
+
+ RID physics_rid;
+
+ bool mesh_owner;
+ uint32_t collision_mask;
+ uint32_t collision_layer;
+ NodePath parent_collision_ignore;
+ PoolVector<PinnedPoint> pinned_points_indices;
+ bool simulation_started;
+ bool pinned_points_cache_dirty;
+
+ Ref<ArrayMesh> debug_mesh_cache;
+ class MeshInstance *debug_mesh;
+
+ bool capture_input_on_drag;
+ bool ray_pickable;
+
+ void _update_pickable();
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ bool _set_property_pinned_points_indices(const Array &p_indices);
+ bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value);
+ bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const;
+
+ virtual void _changed_callback(Object *p_changed, const char *p_prop);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual String get_configuration_warning() const;
+
+protected:
+ void _draw_soft_mesh();
+
+public:
+ void update_physics_server();
+ void become_mesh_owner();
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_layer(uint32_t p_layer);
+ uint32_t get_collision_layer() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
+ void set_collision_layer_bit(int p_bit, bool p_value);
+ bool get_collision_layer_bit(int p_bit) const;
+
+ void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore);
+ const NodePath &get_parent_collision_ignore() const;
+
+ void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices);
+ PoolVector<PinnedPoint> get_pinned_points_indices();
+
+ void set_simulation_precision(int p_simulation_precision);
+ int get_simulation_precision();
+
+ void set_total_mass(real_t p_total_mass);
+ real_t get_total_mass();
+
+ void set_linear_stiffness(real_t p_linear_stiffness);
+ real_t get_linear_stiffness();
+
+ void set_areaAngular_stiffness(real_t p_areaAngular_stiffness);
+ real_t get_areaAngular_stiffness();
+
+ void set_volume_stiffness(real_t p_volume_stiffness);
+ real_t get_volume_stiffness();
+
+ void set_pressure_coefficient(real_t p_pressure_coefficient);
+ real_t get_pressure_coefficient();
+
+ void set_pose_matching_coefficient(real_t p_pose_matching_coefficient);
+ real_t get_pose_matching_coefficient();
+
+ void set_damping_coefficient(real_t p_damping_coefficient);
+ real_t get_damping_coefficient();
+
+ void set_drag_coefficient(real_t p_drag_coefficient);
+ real_t get_drag_coefficient();
+
+ void add_collision_exception_with(Node *p_node);
+ void remove_collision_exception_with(Node *p_node);
+
+ Vector3 get_point_transform(int p_point_index);
+
+ void pin_point_toggle(int p_point_index);
+ void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath());
+ bool is_point_pinned(int p_point_index) const;
+
+ void set_ray_pickable(bool p_ray_pickable);
+ bool is_ray_pickable() const;
+
+ SoftBody();
+ ~SoftBody();
+
+private:
+ void reset_softbody_pin();
+ void _update_cache_pin_points_datas();
+ void _pin_point_on_physics_server(int p_point_index, bool pin);
+ void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path);
+ void _remove_pinned_point(int p_point_index);
+ int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const;
+ int _has_pinned_point(int p_point_index) const;
+};
+
+#endif // SOFT_PHYSICS_BODY_H
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index 748aa8aad4..9b27faed6a 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -280,6 +280,10 @@ Transform Spatial::get_global_transform() const {
data.global_transform = data.local_transform;
}
+ if (data.disable_scale) {
+ data.global_transform.basis.orthonormalize();
+ }
+
data.dirty &= ~DIRTY_GLOBAL;
}
@@ -467,6 +471,15 @@ void Spatial::set_disable_gizmo(bool p_enabled) {
#endif
+void Spatial::set_disable_scale(bool p_enabled) {
+
+ data.disable_scale = p_enabled;
+}
+
+bool Spatial::is_scale_disabled() const {
+ return data.disable_scale;
+}
+
void Spatial::set_as_toplevel(bool p_enabled) {
if (data.toplevel == p_enabled)
@@ -735,6 +748,8 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Spatial::set_ignore_transform_notification);
ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Spatial::set_as_toplevel);
ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
+ ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Spatial::set_disable_scale);
+ ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Spatial::is_scale_disabled);
ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world);
ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo);
@@ -755,15 +770,6 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &Spatial::set_notify_transform);
ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &Spatial::is_transform_notification_enabled);
- void rotate(const Vector3 &p_axis, float p_angle);
- void rotate_x(float p_angle);
- void rotate_y(float p_angle);
- void rotate_z(float p_angle);
- void translate(const Vector3 &p_offset);
- void scale(const Vector3 &p_ratio);
- void global_rotate(const Vector3 &p_axis, float p_angle);
- void global_translate(const Vector3 &p_offset);
-
ClassDB::bind_method(D_METHOD("rotate", "axis", "angle"), &Spatial::rotate);
ClassDB::bind_method(D_METHOD("global_rotate", "axis", "angle"), &Spatial::global_rotate);
ClassDB::bind_method(D_METHOD("global_scale", "scale"), &Spatial::global_scale);
@@ -797,6 +803,7 @@ void Spatial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
+
ADD_GROUP("Visibility", "");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo");
@@ -817,6 +824,7 @@ Spatial::Spatial() :
data.viewport = NULL;
data.inside_world = false;
data.visible = true;
+ data.disable_scale = false;
#ifdef TOOLS_ENABLED
data.gizmo_disabled = false;
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index a43bed3e4a..653714dc98 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -92,6 +92,7 @@ class Spatial : public Node {
bool notify_transform;
bool visible;
+ bool disable_scale;
#ifdef TOOLS_ENABLED
Ref<SpatialGizmo> gizmo;
@@ -153,6 +154,9 @@ public:
void set_as_toplevel(bool p_enabled);
bool is_set_as_toplevel() const;
+ void set_disable_scale(bool p_enabled);
+ bool is_scale_disabled() const;
+
void set_disable_gizmo(bool p_enabled);
void update_gizmo();
void set_gizmo(const Ref<SpatialGizmo> &p_gizmo);
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index 385956dc16..4b870ca54c 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -942,8 +942,6 @@ VehicleBody::VehicleBody() :
engine_force = 0;
brake = 0;
- friction = 1;
-
state = NULL;
ccd = false;
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index 670df5cc7f..ba2807d4e8 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -113,7 +113,7 @@ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
if (min > rad || max < -rad) return false;
- /*======================== Z-tests ========================*/
+/*======================== Z-tests ========================*/
#define AXISTEST_Z12(a, b, fa, fb) \
p1 = a * v1.x - b * v1.y; \
@@ -1961,7 +1961,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh
#endif
for (int i = 0; i < height; i++) {
- //print_line("bake line " + itos(i) + " / " + itos(height));
+ //print_line("bake line " + itos(i) + " / " + itos(height));
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 1)
#endif
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
new file mode 100644
index 0000000000..1bc9fa4b12
--- /dev/null
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -0,0 +1,293 @@
+#include "animation_blend_space_1d.h"
+
+void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) {
+
+ AnimationRootNode::set_tree(p_player);
+
+ for (int i = 0; i < blend_points_used; i++) {
+ blend_points[i].node->set_tree(p_player);
+ }
+}
+
+void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("blend_point_")) {
+ String left = property.name.get_slicec('/', 0);
+ int idx = left.get_slicec('_', 2).to_int();
+ if (idx >= blend_points_used) {
+ property.usage = 0;
+ }
+ }
+ AnimationRootNode::_validate_property(property);
+}
+
+void AnimationNodeBlendSpace1D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position);
+ ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace1D::get_blend_point_position);
+ ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace1D::set_blend_point_node);
+ ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace1D::get_blend_point_node);
+ ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace1D::remove_blend_point);
+ ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace1D::get_blend_point_count);
+
+ ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace1D::set_min_space);
+ ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace1D::get_min_space);
+
+ ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace1D::set_max_space);
+ ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace1D::get_max_space);
+
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap);
+ ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap);
+
+ ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace1D::set_blend_pos);
+ ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace1D::get_blend_pos);
+
+ ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label);
+ ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label);
+
+ ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point);
+
+ for (int i = 0; i < MAX_BLEND_POINTS; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label");
+}
+
+void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) {
+ ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
+ ERR_FAIL_COND(p_node.is_null());
+ ERR_FAIL_COND(p_node->get_parent().is_valid());
+ ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used);
+
+ if (p_at_index == -1 || p_at_index == blend_points_used) {
+ p_at_index = blend_points_used;
+ } else {
+ for (int i = blend_points_used - 1; i > p_at_index; i++) {
+ blend_points[i] = blend_points[i - 1];
+ }
+ }
+
+ blend_points[p_at_index].node = p_node;
+ blend_points[p_at_index].position = p_position;
+
+ blend_points[p_at_index].node->set_parent(this);
+ blend_points[p_at_index].node->set_tree(get_tree());
+
+ blend_points_used++;
+}
+
+void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) {
+ ERR_FAIL_INDEX(p_point, blend_points_used);
+
+ blend_points[p_point].position = p_position;
+}
+
+void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node) {
+ ERR_FAIL_INDEX(p_point, blend_points_used);
+ ERR_FAIL_COND(p_node.is_null());
+
+ if (blend_points[p_point].node.is_valid()) {
+ blend_points[p_point].node->set_parent(NULL);
+ blend_points[p_point].node->set_tree(NULL);
+ }
+
+ blend_points[p_point].node = p_node;
+ blend_points[p_point].node->set_parent(this);
+ blend_points[p_point].node->set_tree(get_tree());
+}
+
+float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const {
+ ERR_FAIL_INDEX_V(p_point, blend_points_used, 0);
+ return blend_points[p_point].position;
+}
+
+Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_point) const {
+ ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref<AnimationRootNode>());
+ return blend_points[p_point].node;
+}
+
+void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
+ ERR_FAIL_INDEX(p_point, blend_points_used);
+
+ blend_points[p_point].node->set_parent(NULL);
+ blend_points[p_point].node->set_tree(NULL);
+
+ for (int i = p_point; i < blend_points_used - 1; i++) {
+ blend_points[i] = blend_points[i + 1];
+ }
+
+ blend_points_used--;
+}
+
+int AnimationNodeBlendSpace1D::get_blend_point_count() const {
+
+ return blend_points_used;
+}
+
+void AnimationNodeBlendSpace1D::set_min_space(float p_min) {
+ min_space = p_min;
+
+ if (min_space >= max_space) {
+ min_space = max_space - 1;
+ }
+}
+
+float AnimationNodeBlendSpace1D::get_min_space() const {
+ return min_space;
+}
+
+void AnimationNodeBlendSpace1D::set_max_space(float p_max) {
+ max_space = p_max;
+
+ if (max_space <= min_space) {
+ max_space = min_space + 1;
+ }
+}
+
+float AnimationNodeBlendSpace1D::get_max_space() const {
+ return max_space;
+}
+
+void AnimationNodeBlendSpace1D::set_snap(float p_snap) {
+ snap = p_snap;
+}
+
+float AnimationNodeBlendSpace1D::get_snap() const {
+ return snap;
+}
+
+void AnimationNodeBlendSpace1D::set_blend_pos(float p_pos) {
+ blend_pos = p_pos;
+}
+
+float AnimationNodeBlendSpace1D::get_blend_pos() const {
+ return blend_pos;
+}
+
+void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) {
+ value_label = p_label;
+}
+
+String AnimationNodeBlendSpace1D::get_value_label() const {
+ return value_label;
+}
+
+void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
+ if (p_index == blend_points_used) {
+ add_blend_point(p_node, 0);
+ } else {
+ set_blend_point_node(p_index, p_node);
+ }
+}
+
+float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) {
+
+ if (blend_points_used == 0) {
+ return 0.0;
+ }
+
+ if (blend_points_used == 1) {
+ // only one point available, just play that animation
+ return blend_node(blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ }
+
+ float weights[MAX_BLEND_POINTS] = {};
+
+ int point_lower = -1;
+ float pos_lower = 0.0;
+ int point_higher = -1;
+ float pos_higher = 0.0;
+
+ // find the closest two points to blend between
+ for (int i = 0; i < blend_points_used; i++) {
+
+ float pos = blend_points[i].position;
+
+ if (pos <= blend_pos) {
+ if (point_lower == -1) {
+ point_lower = i;
+ pos_lower = pos;
+ } else if ((blend_pos - pos) < (blend_pos - pos_lower)) {
+ point_lower = i;
+ pos_lower = pos;
+ }
+ } else {
+ if (point_higher == -1) {
+ point_higher = i;
+ pos_higher = pos;
+ } else if ((pos - blend_pos) < (pos_higher - blend_pos)) {
+ point_higher = i;
+ pos_higher = pos;
+ }
+ }
+ }
+
+ // fill in weights
+
+ if (point_lower == -1) {
+ // we are on the left side, no other point to the left
+ // we just play the next point.
+
+ weights[point_higher] = 1.0;
+ } else if (point_higher == -1) {
+ // we are on the right side, no other point to the right
+ // we just play the previous point
+
+ weights[point_lower] = 1.0;
+ } else {
+
+ // we are between two points.
+ // figure out weights, then blend the animations
+
+ float distance_between_points = pos_higher - pos_lower;
+
+ float current_pos_inbetween = blend_pos - pos_lower;
+
+ float blend_percentage = current_pos_inbetween / distance_between_points;
+
+ float blend_lower = 1.0 - blend_percentage;
+ float blend_higher = blend_percentage;
+
+ weights[point_lower] = blend_lower;
+ weights[point_higher] = blend_higher;
+ }
+
+ // actually blend the animations now
+
+ float max_time_remaining = 0.0;
+
+ for (int i = 0; i < blend_points_used; i++) {
+ float remaining = blend_node(blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
+
+ max_time_remaining = MAX(max_time_remaining, remaining);
+ }
+
+ return max_time_remaining;
+}
+
+String AnimationNodeBlendSpace1D::get_caption() const {
+ return "BlendSpace1D";
+}
+
+AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() {
+
+ blend_points_used = 0;
+ max_space = 1;
+ min_space = -1;
+
+ snap = 0.1;
+ value_label = "value";
+}
+
+AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() {
+
+ for (int i = 0; i < blend_points_used; i++) {
+ blend_points[i].node->set_parent(this);
+ blend_points[i].node->set_tree(get_tree());
+ }
+}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
new file mode 100644
index 0000000000..d1ed4c6a1f
--- /dev/null
+++ b/scene/animation/animation_blend_space_1d.h
@@ -0,0 +1,70 @@
+#ifndef ANIMATION_BLEND_SPACE_1D_H
+#define ANIMATION_BLEND_SPACE_1D_H
+
+#include "scene/animation/animation_tree.h"
+
+class AnimationNodeBlendSpace1D : public AnimationRootNode {
+ GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode)
+
+ enum {
+ MAX_BLEND_POINTS = 64
+ };
+
+ struct BlendPoint {
+ Ref<AnimationRootNode> node;
+ float position;
+ };
+
+ BlendPoint blend_points[MAX_BLEND_POINTS];
+ int blend_points_used;
+
+ float blend_pos;
+
+ float max_space;
+ float min_space;
+
+ float snap;
+
+ String value_label;
+
+ void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
+
+protected:
+ virtual void _validate_property(PropertyInfo &property) const;
+ static void _bind_methods();
+
+public:
+ virtual void set_tree(AnimationTree *p_player);
+
+ void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1);
+ void set_blend_point_position(int p_point, float p_position);
+ void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
+
+ float get_blend_point_position(int p_point) const;
+ Ref<AnimationRootNode> get_blend_point_node(int p_point) const;
+ void remove_blend_point(int p_point);
+ int get_blend_point_count() const;
+
+ void set_min_space(float p_min);
+ float get_min_space() const;
+
+ void set_max_space(float p_max);
+ float get_max_space() const;
+
+ void set_snap(float p_snap);
+ float get_snap() const;
+
+ void set_blend_pos(float p_pos);
+ float get_blend_pos() const;
+
+ void set_value_label(const String &p_label);
+ String get_value_label() const;
+
+ float process(float p_time, bool p_seek);
+ String get_caption() const;
+
+ AnimationNodeBlendSpace1D();
+ ~AnimationNodeBlendSpace1D();
+};
+
+#endif // ANIMATION_BLEND_SPACE_1D_H
diff --git a/scene/animation/animation_blend_space.cpp b/scene/animation/animation_blend_space_2d.cpp
index 44b2d980e2..bba25d64d9 100644
--- a/scene/animation/animation_blend_space.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -1,7 +1,15 @@
-#include "animation_blend_space.h"
+#include "animation_blend_space_2d.h"
#include "math/delaunay.h"
-void AnimationNodeBlendSpace::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
+void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) {
+ AnimationRootNode::set_tree(p_player);
+
+ for (int i = 0; i < blend_points_used; i++) {
+ blend_points[i].node->set_tree(p_player);
+ }
+}
+
+void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_node->get_parent().is_valid());
@@ -10,7 +18,7 @@ void AnimationNodeBlendSpace::add_blend_point(const Ref<AnimationRootNode> &p_no
if (p_at_index == -1 || p_at_index == blend_points_used) {
p_at_index = blend_points_used;
} else {
- for (int i = blend_points_used; i > p_at_index; i--) {
+ for (int i = blend_points_used - 1; i > p_at_index; i--) {
blend_points[i] = blend_points[i - 1];
}
for (int i = 0; i < triangles.size(); i++) {
@@ -25,7 +33,7 @@ void AnimationNodeBlendSpace::add_blend_point(const Ref<AnimationRootNode> &p_no
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].node->set_parent(this);
- blend_points[p_at_index].node->set_graph_player(get_graph_player());
+ blend_points[p_at_index].node->set_tree(get_tree());
blend_points_used++;
if (auto_triangles) {
@@ -33,38 +41,38 @@ void AnimationNodeBlendSpace::add_blend_point(const Ref<AnimationRootNode> &p_no
}
}
-void AnimationNodeBlendSpace::set_blend_point_position(int p_point, const Vector2 &p_position) {
+void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) {
ERR_FAIL_INDEX(p_point, blend_points_used);
blend_points[p_point].position = p_position;
if (auto_triangles) {
trianges_dirty = true;
}
}
-void AnimationNodeBlendSpace::set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node) {
+void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node) {
ERR_FAIL_INDEX(p_point, blend_points_used);
ERR_FAIL_COND(p_node.is_null());
if (blend_points[p_point].node.is_valid()) {
blend_points[p_point].node->set_parent(NULL);
- blend_points[p_point].node->set_graph_player(NULL);
+ blend_points[p_point].node->set_tree(NULL);
}
blend_points[p_point].node = p_node;
blend_points[p_point].node->set_parent(this);
- blend_points[p_point].node->set_graph_player(get_graph_player());
+ blend_points[p_point].node->set_tree(get_tree());
}
-Vector2 AnimationNodeBlendSpace::get_blend_point_position(int p_point) const {
+Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2());
return blend_points[p_point].position;
}
-Ref<AnimationRootNode> AnimationNodeBlendSpace::get_blend_point_node(int p_point) const {
+Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref<AnimationRootNode>());
return blend_points[p_point].node;
}
-void AnimationNodeBlendSpace::remove_blend_point(int p_point) {
+void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
ERR_FAIL_INDEX(p_point, blend_points_used);
blend_points[p_point].node->set_parent(NULL);
- blend_points[p_point].node->set_graph_player(NULL);
+ blend_points[p_point].node->set_tree(NULL);
for (int i = 0; i < triangles.size(); i++) {
bool erase = false;
@@ -89,12 +97,12 @@ void AnimationNodeBlendSpace::remove_blend_point(int p_point) {
blend_points_used--;
}
-int AnimationNodeBlendSpace::get_blend_point_count() const {
+int AnimationNodeBlendSpace2D::get_blend_point_count() const {
return blend_points_used;
}
-bool AnimationNodeBlendSpace::has_triangle(int p_x, int p_y, int p_z) const {
+bool AnimationNodeBlendSpace2D::has_triangle(int p_x, int p_y, int p_z) const {
ERR_FAIL_INDEX_V(p_x, blend_points_used, false);
ERR_FAIL_INDEX_V(p_y, blend_points_used, false);
@@ -123,7 +131,7 @@ bool AnimationNodeBlendSpace::has_triangle(int p_x, int p_y, int p_z) const {
return false;
}
-void AnimationNodeBlendSpace::add_triangle(int p_x, int p_y, int p_z, int p_at_index) {
+void AnimationNodeBlendSpace2D::add_triangle(int p_x, int p_y, int p_z, int p_at_index) {
ERR_FAIL_INDEX(p_x, blend_points_used);
ERR_FAIL_INDEX(p_y, blend_points_used);
@@ -156,7 +164,7 @@ void AnimationNodeBlendSpace::add_triangle(int p_x, int p_y, int p_z, int p_at_i
triangles.insert(p_at_index, t);
}
}
-int AnimationNodeBlendSpace::get_triangle_point(int p_triangle, int p_point) {
+int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) {
_update_triangles();
@@ -164,17 +172,17 @@ int AnimationNodeBlendSpace::get_triangle_point(int p_triangle, int p_point) {
ERR_FAIL_INDEX_V(p_triangle, triangles.size(), -1);
return triangles[p_triangle].points[p_point];
}
-void AnimationNodeBlendSpace::remove_triangle(int p_triangle) {
+void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) {
ERR_FAIL_INDEX(p_triangle, triangles.size());
triangles.remove(p_triangle);
}
-int AnimationNodeBlendSpace::get_triangle_count() const {
+int AnimationNodeBlendSpace2D::get_triangle_count() const {
return triangles.size();
}
-void AnimationNodeBlendSpace::set_min_space(const Vector2 &p_min) {
+void AnimationNodeBlendSpace2D::set_min_space(const Vector2 &p_min) {
min_space = p_min;
if (min_space.x >= max_space.x) {
@@ -184,11 +192,11 @@ void AnimationNodeBlendSpace::set_min_space(const Vector2 &p_min) {
min_space.y = max_space.y - 1;
}
}
-Vector2 AnimationNodeBlendSpace::get_min_space() const {
+Vector2 AnimationNodeBlendSpace2D::get_min_space() const {
return min_space;
}
-void AnimationNodeBlendSpace::set_max_space(const Vector2 &p_max) {
+void AnimationNodeBlendSpace2D::set_max_space(const Vector2 &p_max) {
max_space = p_max;
if (max_space.x <= min_space.x) {
@@ -198,39 +206,39 @@ void AnimationNodeBlendSpace::set_max_space(const Vector2 &p_max) {
max_space.y = min_space.y + 1;
}
}
-Vector2 AnimationNodeBlendSpace::get_max_space() const {
+Vector2 AnimationNodeBlendSpace2D::get_max_space() const {
return max_space;
}
-void AnimationNodeBlendSpace::set_snap(const Vector2 &p_snap) {
+void AnimationNodeBlendSpace2D::set_snap(const Vector2 &p_snap) {
snap = p_snap;
}
-Vector2 AnimationNodeBlendSpace::get_snap() const {
+Vector2 AnimationNodeBlendSpace2D::get_snap() const {
return snap;
}
-void AnimationNodeBlendSpace::set_blend_pos(const Vector2 &p_pos) {
+void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) {
blend_pos = p_pos;
}
-Vector2 AnimationNodeBlendSpace::get_blend_pos() const {
+Vector2 AnimationNodeBlendSpace2D::get_blend_position() const {
return blend_pos;
}
-void AnimationNodeBlendSpace::set_x_label(const String &p_label) {
+void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) {
x_label = p_label;
}
-String AnimationNodeBlendSpace::get_x_label() const {
+String AnimationNodeBlendSpace2D::get_x_label() const {
return x_label;
}
-void AnimationNodeBlendSpace::set_y_label(const String &p_label) {
+void AnimationNodeBlendSpace2D::set_y_label(const String &p_label) {
y_label = p_label;
}
-String AnimationNodeBlendSpace::get_y_label() const {
+String AnimationNodeBlendSpace2D::get_y_label() const {
return y_label;
}
-void AnimationNodeBlendSpace::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
+void AnimationNodeBlendSpace2D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
if (p_index == blend_points_used) {
add_blend_point(p_node, Vector2());
} else {
@@ -238,7 +246,7 @@ void AnimationNodeBlendSpace::_add_blend_point(int p_index, const Ref<AnimationR
}
}
-void AnimationNodeBlendSpace::_set_triangles(const Vector<int> &p_triangles) {
+void AnimationNodeBlendSpace2D::_set_triangles(const Vector<int> &p_triangles) {
if (auto_triangles)
return;
@@ -248,7 +256,7 @@ void AnimationNodeBlendSpace::_set_triangles(const Vector<int> &p_triangles) {
}
}
-Vector<int> AnimationNodeBlendSpace::_get_triangles() const {
+Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
Vector<int> t;
if (auto_triangles && trianges_dirty)
@@ -263,7 +271,7 @@ Vector<int> AnimationNodeBlendSpace::_get_triangles() const {
return t;
}
-void AnimationNodeBlendSpace::_update_triangles() {
+void AnimationNodeBlendSpace2D::_update_triangles() {
if (!auto_triangles || !trianges_dirty)
return;
@@ -286,7 +294,7 @@ void AnimationNodeBlendSpace::_update_triangles() {
}
}
-Vector2 AnimationNodeBlendSpace::get_closest_point(const Vector2 &p_point) {
+Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) {
_update_triangles();
@@ -323,7 +331,7 @@ Vector2 AnimationNodeBlendSpace::get_closest_point(const Vector2 &p_point) {
return best_point;
}
-void AnimationNodeBlendSpace::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) {
+void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) {
if (p_pos.distance_squared_to(p_points[0]) < CMP_EPSILON2) {
r_weights[0] = 1;
@@ -369,7 +377,7 @@ void AnimationNodeBlendSpace::_blend_triangle(const Vector2 &p_pos, const Vector
r_weights[2] = w;
}
-float AnimationNodeBlendSpace::process(float p_time, bool p_seek) {
+float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) {
_update_triangles();
@@ -453,11 +461,11 @@ float AnimationNodeBlendSpace::process(float p_time, bool p_seek) {
return mind;
}
-String AnimationNodeBlendSpace::get_caption() const {
- return "BlendSpace";
+String AnimationNodeBlendSpace2D::get_caption() const {
+ return "BlendSpace2D";
}
-void AnimationNodeBlendSpace::_validate_property(PropertyInfo &property) const {
+void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &property) const {
if (property.name.begins_with("blend_point_")) {
String left = property.name.get_slicec('/', 0);
int idx = left.get_slicec('_', 2).to_int();
@@ -468,57 +476,57 @@ void AnimationNodeBlendSpace::_validate_property(PropertyInfo &property) const {
AnimationRootNode::_validate_property(property);
}
-void AnimationNodeBlendSpace::set_auto_triangles(bool p_enable) {
+void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) {
auto_triangles = p_enable;
if (auto_triangles) {
trianges_dirty = true;
}
}
-bool AnimationNodeBlendSpace::get_auto_triangles() const {
+bool AnimationNodeBlendSpace2D::get_auto_triangles() const {
return auto_triangles;
}
-void AnimationNodeBlendSpace::_bind_methods() {
+void AnimationNodeBlendSpace2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace::add_blend_point, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace::set_blend_point_position);
- ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace::get_blend_point_position);
- ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace::set_blend_point_node);
- ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace::get_blend_point_node);
- ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace::remove_blend_point);
- ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace::get_blend_point_count);
+ ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
+ ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace2D::get_blend_point_position);
+ ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace2D::set_blend_point_node);
+ ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace2D::get_blend_point_node);
+ ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace2D::remove_blend_point);
+ ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace2D::get_blend_point_count);
- ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace::add_triangle, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace::get_triangle_point);
- ClassDB::bind_method(D_METHOD("remove_triangle", "triangle"), &AnimationNodeBlendSpace::remove_triangle);
- ClassDB::bind_method(D_METHOD("get_triangle_count"), &AnimationNodeBlendSpace::get_triangle_count);
+ ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace2D::add_triangle, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace2D::get_triangle_point);
+ ClassDB::bind_method(D_METHOD("remove_triangle", "triangle"), &AnimationNodeBlendSpace2D::remove_triangle);
+ ClassDB::bind_method(D_METHOD("get_triangle_count"), &AnimationNodeBlendSpace2D::get_triangle_count);
- ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace::set_min_space);
- ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace::get_min_space);
+ ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace2D::set_min_space);
+ ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace2D::get_min_space);
- ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace::set_max_space);
- ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace::get_max_space);
+ ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace2D::set_max_space);
+ ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace2D::get_max_space);
- ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace::set_snap);
- ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace::get_snap);
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap);
+ ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap);
- ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace::set_blend_pos);
- ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace::get_blend_pos);
+ ClassDB::bind_method(D_METHOD("set_blend_position", "pos"), &AnimationNodeBlendSpace2D::set_blend_position);
+ ClassDB::bind_method(D_METHOD("get_blend_position"), &AnimationNodeBlendSpace2D::get_blend_position);
- ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace::set_x_label);
- ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace::get_x_label);
+ ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label);
+ ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label);
- ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace::set_y_label);
- ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace::get_y_label);
+ ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace2D::set_y_label);
+ ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace2D::get_y_label);
- ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace::_add_blend_point);
+ ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace2D::_add_blend_point);
- ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace::_set_triangles);
- ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace::_get_triangles);
+ ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace2D::_set_triangles);
+ ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace2D::_get_triangles);
- ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace::set_auto_triangles);
- ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace::get_auto_triangles);
+ ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles);
+ ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles");
@@ -532,12 +540,12 @@ void AnimationNodeBlendSpace::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_position", "get_blend_position");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label");
}
-AnimationNodeBlendSpace::AnimationNodeBlendSpace() {
+AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
auto_triangles = true;
blend_points_used = 0;
@@ -549,10 +557,10 @@ AnimationNodeBlendSpace::AnimationNodeBlendSpace() {
trianges_dirty = false;
}
-AnimationNodeBlendSpace::~AnimationNodeBlendSpace() {
+AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {
for (int i = 0; i < blend_points_used; i++) {
blend_points[i].node->set_parent(this);
- blend_points[i].node->set_graph_player(get_graph_player());
+ blend_points[i].node->set_tree(get_tree());
}
}
diff --git a/scene/animation/animation_blend_space.h b/scene/animation/animation_blend_space_2d.h
index 3eda3d2d39..74d20b6013 100644
--- a/scene/animation/animation_blend_space.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -1,10 +1,10 @@
-#ifndef ANIMATION_BLEND_SPACE_H
-#define ANIMATION_BLEND_SPACE_H
+#ifndef ANIMATION_BLEND_SPACE_2D_H
+#define ANIMATION_BLEND_SPACE_2D_H
-#include "scene/animation/animation_graph_player.h"
+#include "scene/animation/animation_tree.h"
-class AnimationNodeBlendSpace : public AnimationRootNode {
- GDCLASS(AnimationNodeBlendSpace, AnimationRootNode)
+class AnimationNodeBlendSpace2D : public AnimationRootNode {
+ GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode)
enum {
MAX_BLEND_POINTS = 64
@@ -47,6 +47,8 @@ protected:
static void _bind_methods();
public:
+ virtual void set_tree(AnimationTree *p_player);
+
void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1);
void set_blend_point_position(int p_point, const Vector2 &p_position);
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
@@ -70,8 +72,8 @@ public:
void set_snap(const Vector2 &p_snap);
Vector2 get_snap() const;
- void set_blend_pos(const Vector2 &p_pos);
- Vector2 get_blend_pos() const;
+ void set_blend_position(const Vector2 &p_pos);
+ Vector2 get_blend_position() const;
void set_x_label(const String &p_label);
String get_x_label() const;
@@ -79,16 +81,16 @@ public:
void set_y_label(const String &p_label);
String get_y_label() const;
- float process(float p_time, bool p_seek);
- String get_caption() const;
+ virtual float process(float p_time, bool p_seek);
+ virtual String get_caption() const;
Vector2 get_closest_point(const Vector2 &p_point);
void set_auto_triangles(bool p_enable);
bool get_auto_triangles() const;
- AnimationNodeBlendSpace();
- ~AnimationNodeBlendSpace();
+ AnimationNodeBlendSpace2D();
+ ~AnimationNodeBlendSpace2D();
};
-#endif // ANIMATION_BLEND_SPACE_H
+#endif // ANIMATION_BLEND_SPACE_2D_H
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index ced0cbbbba..65904410d3 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -16,7 +16,7 @@ float AnimationNodeAnimation::get_playback_time() const {
void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
if (property.name == "animation") {
- AnimationGraphPlayer *gp = get_graph_player();
+ AnimationTree *gp = get_tree();
if (gp && gp->has_node(gp->get_animation_player())) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player()));
if (ap) {
@@ -288,8 +288,8 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
- BIND_CONSTANT(MIX_MODE_BLEND)
- BIND_CONSTANT(MIX_MODE_ADD)
+ BIND_ENUM_CONSTANT(MIX_MODE_BLEND)
+ BIND_ENUM_CONSTANT(MIX_MODE_ADD)
}
AnimationNodeOneShot::AnimationNodeOneShot() {
@@ -311,33 +311,33 @@ AnimationNodeOneShot::AnimationNodeOneShot() {
////////////////////////////////////////////////
-void AnimationNodeAdd::set_amount(float p_amount) {
+void AnimationNodeAdd2::set_amount(float p_amount) {
amount = p_amount;
}
-float AnimationNodeAdd::get_amount() const {
+float AnimationNodeAdd2::get_amount() const {
return amount;
}
-String AnimationNodeAdd::get_caption() const {
- return "Add";
+String AnimationNodeAdd2::get_caption() const {
+ return "Add2";
}
-void AnimationNodeAdd::set_use_sync(bool p_sync) {
+void AnimationNodeAdd2::set_use_sync(bool p_sync) {
sync = p_sync;
}
-bool AnimationNodeAdd::is_using_sync() const {
+bool AnimationNodeAdd2::is_using_sync() const {
return sync;
}
-bool AnimationNodeAdd::has_filter() const {
+bool AnimationNodeAdd2::has_filter() const {
return true;
}
-float AnimationNodeAdd::process(float p_time, bool p_seek) {
+float AnimationNodeAdd2::process(float p_time, bool p_seek) {
float rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
@@ -345,19 +345,19 @@ float AnimationNodeAdd::process(float p_time, bool p_seek) {
return rem0;
}
-void AnimationNodeAdd::_bind_methods() {
+void AnimationNodeAdd2::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd::set_amount);
- ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd::get_amount);
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd2::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd2::get_amount);
- ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd::set_use_sync);
- ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd::is_using_sync);
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
}
-AnimationNodeAdd::AnimationNodeAdd() {
+AnimationNodeAdd2::AnimationNodeAdd2() {
add_input("in");
add_input("add");
@@ -365,6 +365,63 @@ AnimationNodeAdd::AnimationNodeAdd() {
sync = false;
}
+////////////////////////////////////////////////
+
+void AnimationNodeAdd3::set_amount(float p_amount) {
+ amount = p_amount;
+}
+
+float AnimationNodeAdd3::get_amount() const {
+ return amount;
+}
+
+String AnimationNodeAdd3::get_caption() const {
+ return "Add3";
+}
+void AnimationNodeAdd3::set_use_sync(bool p_sync) {
+
+ sync = p_sync;
+}
+
+bool AnimationNodeAdd3::is_using_sync() const {
+
+ return sync;
+}
+
+bool AnimationNodeAdd3::has_filter() const {
+
+ return true;
+}
+
+float AnimationNodeAdd3::process(float p_time, bool p_seek) {
+
+ blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
+ float rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
+
+ return rem0;
+}
+
+void AnimationNodeAdd3::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd3::set_amount);
+ ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd3::get_amount);
+
+ ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync);
+ ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
+}
+
+AnimationNodeAdd3::AnimationNodeAdd3() {
+
+ add_input("-add");
+ add_input("in");
+ add_input("+add");
+ amount = 0;
+ sync = false;
+}
/////////////////////////////////////////////
void AnimationNodeBlend2::set_amount(float p_amount) {
@@ -762,13 +819,13 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
ERR_FAIL_COND(nodes.has(p_name));
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_node->get_parent().is_valid());
- ERR_FAIL_COND(p_node->get_graph_player() != NULL);
+ ERR_FAIL_COND(p_node->get_tree() != NULL);
ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
ERR_FAIL_COND(String(p_name).find("/") != -1);
nodes[p_name] = p_node;
p_node->set_parent(this);
- p_node->set_graph_player(get_graph_player());
+ p_node->set_tree(get_tree());
emit_changed();
}
@@ -804,7 +861,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
node->set_input_connection(i, StringName());
}
node->set_parent(NULL);
- node->set_graph_player(NULL);
+ node->set_tree(NULL);
}
nodes.erase(p_name);
@@ -965,13 +1022,13 @@ Vector2 AnimationNodeBlendTree::get_graph_offset() const {
return graph_offset;
}
-void AnimationNodeBlendTree::set_graph_player(AnimationGraphPlayer *p_player) {
+void AnimationNodeBlendTree::set_tree(AnimationTree *p_player) {
- AnimationNode::set_graph_player(p_player);
+ AnimationNode::set_tree(p_player);
for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
Ref<AnimationNode> node = E->get();
- node->set_graph_player(p_player);
+ node->set_tree(p_player);
}
}
@@ -979,6 +1036,7 @@ bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_val
String name = p_name;
if (name.begins_with("nodes/")) {
+
String node_name = name.get_slicec('/', 1);
String what = name.get_slicec('/', 2);
@@ -1107,6 +1165,6 @@ AnimationNodeBlendTree::~AnimationNodeBlendTree() {
for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) {
E->get()->set_parent(NULL);
- E->get()->set_graph_player(NULL);
+ E->get()->set_tree(NULL);
}
}
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 7623fd3e57..e86cc2e823 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -1,7 +1,7 @@
#ifndef ANIMATION_BLEND_TREE_H
#define ANIMATION_BLEND_TREE_H
-#include "scene/animation/animation_graph_player.h"
+#include "scene/animation/animation_tree.h"
class AnimationNodeAnimation : public AnimationRootNode {
@@ -94,8 +94,8 @@ public:
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
-class AnimationNodeAdd : public AnimationNode {
- GDCLASS(AnimationNodeAdd, AnimationNode);
+class AnimationNodeAdd2 : public AnimationNode {
+ GDCLASS(AnimationNodeAdd2, AnimationNode);
float amount;
bool sync;
@@ -115,7 +115,31 @@ public:
virtual bool has_filter() const;
virtual float process(float p_time, bool p_seek);
- AnimationNodeAdd();
+ AnimationNodeAdd2();
+};
+
+class AnimationNodeAdd3 : public AnimationNode {
+ GDCLASS(AnimationNodeAdd3, AnimationNode);
+
+ float amount;
+ bool sync;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_amount(float p_amount);
+ float get_amount() const;
+
+ void set_use_sync(bool p_sync);
+ bool is_using_sync() const;
+
+ virtual bool has_filter() const;
+ virtual float process(float p_time, bool p_seek);
+
+ AnimationNodeAdd3();
};
class AnimationNodeBlend2 : public AnimationNode {
@@ -318,7 +342,7 @@ public:
void set_graph_offset(const Vector2 &p_graph_offset);
Vector2 get_graph_offset() const;
- virtual void set_graph_player(AnimationGraphPlayer *p_player);
+ virtual void set_tree(AnimationTree *p_player);
AnimationNodeBlendTree();
~AnimationNodeBlendTree();
};
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
new file mode 100644
index 0000000000..36587a1e91
--- /dev/null
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -0,0 +1,790 @@
+#include "animation_node_state_machine.h"
+
+/////////////////////////////////////////////////
+
+void AnimationNodeStateMachineTransition::set_switch_mode(SwitchMode p_mode) {
+
+ switch_mode = p_mode;
+}
+
+AnimationNodeStateMachineTransition::SwitchMode AnimationNodeStateMachineTransition::get_switch_mode() const {
+
+ return switch_mode;
+}
+
+void AnimationNodeStateMachineTransition::set_auto_advance(bool p_enable) {
+ auto_advance = p_enable;
+}
+
+bool AnimationNodeStateMachineTransition::has_auto_advance() const {
+ return auto_advance;
+}
+
+void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) {
+
+ ERR_FAIL_COND(p_xfade < 0);
+ xfade = p_xfade;
+ emit_changed();
+}
+
+float AnimationNodeStateMachineTransition::get_xfade_time() const {
+ return xfade;
+}
+
+void AnimationNodeStateMachineTransition::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+ emit_changed();
+}
+
+bool AnimationNodeStateMachineTransition::is_disabled() const {
+ return disabled;
+}
+
+void AnimationNodeStateMachineTransition::set_priority(int p_priority) {
+ priority = p_priority;
+ emit_changed();
+}
+
+int AnimationNodeStateMachineTransition::get_priority() const {
+ return priority;
+}
+
+void AnimationNodeStateMachineTransition::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_switch_mode", "mode"), &AnimationNodeStateMachineTransition::set_switch_mode);
+ ClassDB::bind_method(D_METHOD("get_switch_mode"), &AnimationNodeStateMachineTransition::get_switch_mode);
+
+ ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance);
+ ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance);
+
+ ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time);
+ ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time);
+
+ ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &AnimationNodeStateMachineTransition::set_disabled);
+ ClassDB::bind_method(D_METHOD("is_disabled"), &AnimationNodeStateMachineTransition::is_disabled);
+
+ ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
+ ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+
+ BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
+ BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC);
+ BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END);
+}
+
+AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() {
+
+ switch_mode = SWITCH_MODE_IMMEDIATE;
+ auto_advance = false;
+ xfade = 0;
+ disabled = false;
+ priority = 1;
+}
+
+///////////////////////////////////////////////////////
+void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node) {
+
+ ERR_FAIL_COND(states.has(p_name));
+ ERR_FAIL_COND(p_node.is_null());
+ ERR_FAIL_COND(p_node->get_parent().is_valid());
+ ERR_FAIL_COND(p_node->get_tree() != NULL);
+ ERR_FAIL_COND(String(p_name).find("/") != -1);
+ states[p_name] = p_node;
+
+ p_node->set_parent(this);
+ p_node->set_tree(get_tree());
+
+ emit_changed();
+}
+
+Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const {
+
+ ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>());
+
+ return states[p_name];
+}
+
+StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const {
+ for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) {
+ if (E->get() == p_node) {
+ return E->key();
+ }
+ }
+
+ ERR_FAIL_V(StringName());
+}
+
+bool AnimationNodeStateMachine::has_node(const StringName &p_name) const {
+ return states.has(p_name);
+}
+void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
+
+ ERR_FAIL_COND(!states.has(p_name));
+
+ {
+ //erase node connections
+ Ref<AnimationNode> node = states[p_name];
+ for (int i = 0; i < node->get_input_count(); i++) {
+ node->set_input_connection(i, StringName());
+ }
+ node->set_parent(NULL);
+ node->set_tree(NULL);
+ }
+
+ states.erase(p_name);
+ path.erase(p_name);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_name || transitions[i].to == p_name) {
+ transitions.remove(i);
+ i--;
+ }
+ }
+
+ if (start_node == p_name) {
+ start_node = StringName();
+ }
+
+ if (end_node == p_name) {
+ end_node = StringName();
+ }
+
+ if (playing && current == p_name) {
+ stop();
+ }
+ emit_changed();
+}
+
+void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) {
+
+ ERR_FAIL_COND(!states.has(p_name));
+ ERR_FAIL_COND(states.has(p_new_name));
+
+ states[p_new_name] = states[p_name];
+ states.erase(p_name);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_name) {
+ transitions[i].from = p_new_name;
+ }
+
+ if (transitions[i].to == p_name) {
+ transitions[i].to = p_new_name;
+ }
+ }
+
+ if (start_node == p_name) {
+ start_node = p_new_name;
+ }
+
+ if (end_node == p_name) {
+ end_node = p_new_name;
+ }
+
+ if (playing && current == p_name) {
+ current = p_new_name;
+ }
+
+ path.clear(); //clear path
+}
+
+void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
+
+ List<StringName> nodes;
+ for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) {
+ nodes.push_back(E->key());
+ }
+ nodes.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
+ r_nodes->push_back(E->get());
+ }
+}
+
+bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const {
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_from && transitions[i].to == p_to)
+ return true;
+ }
+ return false;
+}
+
+int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const {
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_from && transitions[i].to == p_to)
+ return i;
+ }
+ return -1;
+}
+
+void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) {
+
+ ERR_FAIL_COND(p_from == p_to);
+ ERR_FAIL_COND(!states.has(p_from));
+ ERR_FAIL_COND(!states.has(p_to));
+ ERR_FAIL_COND(p_transition.is_null());
+
+ for (int i = 0; i < transitions.size(); i++) {
+ ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to);
+ }
+
+ Transition tr;
+ tr.from = p_from;
+ tr.to = p_to;
+ tr.transition = p_transition;
+
+ transitions.push_back(tr);
+}
+
+Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const {
+ ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>());
+ return transitions[p_transition].transition;
+}
+StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const {
+
+ ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName());
+ return transitions[p_transition].from;
+}
+StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const {
+
+ ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName());
+ return transitions[p_transition].to;
+}
+
+int AnimationNodeStateMachine::get_transition_count() const {
+
+ return transitions.size();
+}
+void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) {
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ transitions.remove(i);
+ return;
+ }
+ }
+
+ if (playing) {
+ path.clear();
+ }
+}
+
+void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) {
+
+ transitions.remove(p_transition);
+ if (playing) {
+ path.clear();
+ }
+}
+
+void AnimationNodeStateMachine::set_start_node(const StringName &p_node) {
+
+ ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
+ start_node = p_node;
+}
+
+String AnimationNodeStateMachine::get_start_node() const {
+
+ return start_node;
+}
+
+void AnimationNodeStateMachine::set_end_node(const StringName &p_node) {
+
+ ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
+ end_node = p_node;
+}
+
+String AnimationNodeStateMachine::get_end_node() const {
+
+ return end_node;
+}
+
+void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) {
+ graph_offset = p_offset;
+}
+
+Vector2 AnimationNodeStateMachine::get_graph_offset() const {
+ return graph_offset;
+}
+
+float AnimationNodeStateMachine::process(float p_time, bool p_seek) {
+
+ //if not playing and it can restart, then restart
+ if (!playing) {
+ if (start_node) {
+ start(start_node);
+ } else {
+ return 0;
+ }
+ }
+
+ bool do_start = (p_seek && p_time == 0) || play_start || current == StringName();
+
+ if (do_start) {
+
+ if (start_node != StringName() && p_seek && p_time == 0) {
+ current = start_node;
+ }
+
+ len_current = blend_node(states[current], 0, true, 1.0, FILTER_IGNORE, false);
+ pos_current = 0;
+ loops_current = 0;
+ play_start = false;
+ }
+
+ float fade_blend = 1.0;
+
+ if (fading_from != StringName()) {
+
+ if (!p_seek) {
+ fading_pos += p_time;
+ }
+ fade_blend = MIN(1.0, fading_pos / fading_time);
+ if (fade_blend >= 1.0) {
+ fading_from = StringName();
+ }
+ }
+
+ float rem = blend_node(states[current], p_time, p_seek, fade_blend, FILTER_IGNORE, false);
+
+ if (fading_from != StringName()) {
+
+ blend_node(states[fading_from], p_time, p_seek, 1.0 - fade_blend, FILTER_IGNORE, false);
+ }
+
+ //guess playback position
+ if (rem > len_current) { // weird but ok
+ len_current = rem;
+ }
+
+ { //advance and loop check
+
+ float next_pos = len_current - rem;
+
+ if (next_pos < pos_current) {
+ loops_current++;
+ }
+ pos_current = next_pos; //looped
+ }
+
+ //find next
+ StringName next;
+ float next_xfade = 0;
+ AnimationNodeStateMachineTransition::SwitchMode switch_mode = AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE;
+
+ if (path.size()) {
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == current && transitions[i].to == path[0]) {
+ next_xfade = transitions[i].transition->get_xfade_time();
+ switch_mode = transitions[i].transition->get_switch_mode();
+ next = path[0];
+ }
+ }
+ } else {
+ float priority_best = 1e20;
+ int auto_advance_to = -1;
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == current && transitions[i].transition->has_auto_advance()) {
+
+ if (transitions[i].transition->get_priority() < priority_best) {
+ auto_advance_to = i;
+ }
+ }
+ }
+
+ if (auto_advance_to != -1) {
+ next = transitions[auto_advance_to].to;
+ next_xfade = transitions[auto_advance_to].transition->get_xfade_time();
+ switch_mode = transitions[auto_advance_to].transition->get_switch_mode();
+ }
+ }
+
+ //if next, see when to transition
+ if (next != StringName()) {
+
+ bool goto_next = false;
+
+ if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE) {
+ goto_next = fading_from == StringName();
+ } else {
+ goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0;
+ if (loops_current > 0) {
+ next_xfade = 0;
+ }
+ }
+
+ if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped
+
+ if (next_xfade) {
+ //time to fade, baby
+ fading_from = current;
+ fading_time = next_xfade;
+ fading_pos = 0;
+ } else {
+ fading_from = StringName();
+ fading_pos = 0;
+ }
+
+ if (path.size()) { //if it came from path, remove path
+ path.remove(0);
+ }
+ current = next;
+ if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
+ len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false);
+ pos_current = MIN(pos_current, len_current);
+ blend_node(states[current], pos_current, true, 0, FILTER_IGNORE, false);
+
+ } else {
+ len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false);
+ pos_current = 0;
+ }
+
+ rem = len_current; //so it does not show 0 on transition
+ loops_current = 0;
+ }
+ }
+
+ //compute time left for transitions by using the end node
+
+ if (end_node != StringName() && end_node != current) {
+
+ rem = blend_node(states[end_node], 0, true, 0, FILTER_IGNORE, false);
+ }
+
+ return rem;
+}
+
+bool AnimationNodeStateMachine::travel(const StringName &p_state) {
+ ERR_FAIL_COND_V(!playing, false);
+ ERR_FAIL_COND_V(!states.has(p_state), false);
+ ERR_FAIL_COND_V(!states.has(current), false);
+
+ path.clear(); //a new one will be needed
+
+ if (current == p_state)
+ return true; //nothing to do
+
+ loops_current = 0; // reset loops, so fade does not happen immediately
+
+ Vector2 current_pos = states[current]->get_position();
+ Vector2 target_pos = states[p_state]->get_position();
+
+ Map<StringName, AStarCost> cost_map;
+
+ List<int> open_list;
+
+ //build open list
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == current) {
+ open_list.push_back(i);
+ float cost = states[transitions[i].to]->get_position().distance_to(current_pos);
+ cost *= transitions[i].transition->get_priority();
+ AStarCost ap;
+ ap.prev = current;
+ ap.distance = cost;
+ cost_map[transitions[i].to] = ap;
+
+ if (transitions[i].to == p_state) { //prematurely found it! :D
+ path.push_back(p_state);
+ return true;
+ }
+ }
+ }
+
+ //begin astar
+ bool found_route = false;
+ while (!found_route) {
+
+ if (open_list.size() == 0) {
+ return false; //no path found
+ }
+
+ //find the last cost transition
+ List<int>::Element *least_cost_transition = NULL;
+ float least_cost = 1e20;
+
+ for (List<int>::Element *E = open_list.front(); E; E = E->next()) {
+
+ float cost = cost_map[transitions[E->get()].to].distance;
+ cost += states[transitions[E->get()].to]->get_position().distance_to(target_pos);
+
+ if (cost < least_cost) {
+ least_cost_transition = E;
+ }
+ }
+
+ StringName transition_prev = transitions[least_cost_transition->get()].from;
+ StringName transition = transitions[least_cost_transition->get()].to;
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from != transition || transitions[i].to == transition_prev) {
+ continue; //not interested on those
+ }
+
+ float distance = states[transitions[i].from]->get_position().distance_to(states[transitions[i].to]->get_position());
+ distance *= transitions[i].transition->get_priority();
+ distance += cost_map[transitions[i].from].distance;
+
+ if (cost_map.has(transitions[i].to)) {
+ //oh this was visited already, can we win the cost?
+ if (distance < cost_map[transitions[i].to].distance) {
+ cost_map[transitions[i].to].distance = distance;
+ cost_map[transitions[i].to].prev = transitions[i].from;
+ }
+ } else {
+ //add to open list
+ AStarCost ac;
+ ac.prev = transitions[i].from;
+ ac.distance = distance;
+ cost_map[transitions[i].to] = ac;
+
+ open_list.push_back(i);
+
+ if (transitions[i].to == p_state) {
+ found_route = true;
+ break;
+ }
+ }
+ }
+
+ if (found_route) {
+ break;
+ }
+
+ open_list.erase(least_cost_transition);
+ }
+
+ //make path
+ StringName at = p_state;
+ while (at != current) {
+ path.push_back(at);
+ at = cost_map[at].prev;
+ }
+
+ path.invert();
+
+ return true;
+}
+
+void AnimationNodeStateMachine::start(const StringName &p_state) {
+
+ ERR_FAIL_COND(!states.has(p_state));
+ path.clear();
+ current = p_state;
+ playing = true;
+ play_start = true;
+}
+void AnimationNodeStateMachine::stop() {
+ playing = false;
+ play_start = false;
+ current = StringName();
+}
+bool AnimationNodeStateMachine::is_playing() const {
+
+ return playing;
+}
+StringName AnimationNodeStateMachine::get_current_node() const {
+ if (!playing) {
+ return StringName();
+ }
+
+ return current;
+}
+
+StringName AnimationNodeStateMachine::get_blend_from_node() const {
+ if (!playing) {
+ return StringName();
+ }
+
+ return fading_from;
+}
+
+float AnimationNodeStateMachine::get_current_play_pos() const {
+ return pos_current;
+}
+float AnimationNodeStateMachine::get_current_length() const {
+ return len_current;
+}
+
+Vector<StringName> AnimationNodeStateMachine::get_travel_path() const {
+ return path;
+}
+String AnimationNodeStateMachine::get_caption() const {
+ return "StateMachine";
+}
+
+void AnimationNodeStateMachine::_notification(int p_what) {
+}
+
+void AnimationNodeStateMachine::set_tree(AnimationTree *p_player) {
+
+ AnimationNode::set_tree(p_player);
+
+ for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) {
+ Ref<AnimationRootNode> node = E->get();
+ node->set_tree(p_player);
+ }
+}
+
+bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) {
+
+ String name = p_name;
+ if (name.begins_with("states/")) {
+ String node_name = name.get_slicec('/', 1);
+ String what = name.get_slicec('/', 2);
+
+ if (what == "node") {
+ Ref<AnimationNode> anode = p_value;
+ if (anode.is_valid()) {
+ add_node(node_name, p_value);
+ }
+ return true;
+ }
+
+ if (what == "position") {
+
+ if (states.has(node_name)) {
+ states[node_name]->set_position(p_value);
+ }
+ return true;
+ }
+ } else if (name == "transitions") {
+
+ Array trans = p_value;
+ ERR_FAIL_COND_V(trans.size() % 3 != 0, false);
+
+ for (int i = 0; i < trans.size(); i += 3) {
+ add_transition(trans[i], trans[i + 1], trans[i + 2]);
+ }
+ return true;
+ } else if (name == "start_node") {
+ set_start_node(p_value);
+ return true;
+ } else if (name == "end_node") {
+ set_end_node(p_value);
+ return true;
+ } else if (name == "graph_offset") {
+ set_graph_offset(p_value);
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String name = p_name;
+ if (name.begins_with("states/")) {
+ String node_name = name.get_slicec('/', 1);
+ String what = name.get_slicec('/', 2);
+
+ if (what == "node") {
+ if (states.has(node_name)) {
+ r_ret = states[node_name];
+ return true;
+ }
+ }
+
+ if (what == "position") {
+
+ if (states.has(node_name)) {
+ r_ret = states[node_name]->get_position();
+ return true;
+ }
+ }
+ } else if (name == "transitions") {
+ Array trans;
+ trans.resize(transitions.size() * 3);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ trans[i * 3 + 0] = transitions[i].from;
+ trans[i * 3 + 1] = transitions[i].to;
+ trans[i * 3 + 2] = transitions[i].transition;
+ }
+
+ r_ret = trans;
+ return true;
+ } else if (name == "start_node") {
+ r_ret = get_start_node();
+ return true;
+ } else if (name == "end_node") {
+ r_ret = get_end_node();
+ return true;
+ } else if (name == "graph_offset") {
+ r_ret = get_graph_offset();
+ return true;
+ }
+
+ return false;
+}
+void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ List<StringName> names;
+ for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) {
+ names.push_back(E->key());
+ }
+ names.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ String name = E->get();
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+}
+
+void AnimationNodeStateMachine::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeStateMachine::add_node);
+ ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node);
+ ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeStateMachine::remove_node);
+ ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeStateMachine::rename_node);
+ ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeStateMachine::has_node);
+ ClassDB::bind_method(D_METHOD("get_node_name", "node"), &AnimationNodeStateMachine::get_node_name);
+
+ ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition);
+ ClassDB::bind_method(D_METHOD("add_transition", "from", "to", "transition"), &AnimationNodeStateMachine::add_transition);
+ ClassDB::bind_method(D_METHOD("get_transition", "idx"), &AnimationNodeStateMachine::get_transition);
+ ClassDB::bind_method(D_METHOD("get_transition_from", "idx"), &AnimationNodeStateMachine::get_transition_from);
+ ClassDB::bind_method(D_METHOD("get_transition_to", "idx"), &AnimationNodeStateMachine::get_transition_to);
+ ClassDB::bind_method(D_METHOD("get_transition_count"), &AnimationNodeStateMachine::get_transition_count);
+ ClassDB::bind_method(D_METHOD("remove_transition_by_index", "idx"), &AnimationNodeStateMachine::remove_transition_by_index);
+ ClassDB::bind_method(D_METHOD("remove_transition", "from", "to"), &AnimationNodeStateMachine::remove_transition);
+
+ ClassDB::bind_method(D_METHOD("set_start_node", "name"), &AnimationNodeStateMachine::set_start_node);
+ ClassDB::bind_method(D_METHOD("get_start_node"), &AnimationNodeStateMachine::get_start_node);
+
+ ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node);
+ ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node);
+
+ ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset);
+ ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset);
+
+ ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachine::travel);
+ ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachine::start);
+ ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachine::stop);
+ ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachine::is_playing);
+ ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachine::get_current_node);
+ ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachine::get_travel_path);
+}
+
+AnimationNodeStateMachine::AnimationNodeStateMachine() {
+
+ play_start = false;
+
+ playing = false;
+ len_current = 0;
+
+ fading_time = 0;
+}
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
new file mode 100644
index 0000000000..e7357e09ea
--- /dev/null
+++ b/scene/animation/animation_node_state_machine.h
@@ -0,0 +1,142 @@
+#ifndef ANIMATION_NODE_STATE_MACHINE_H
+#define ANIMATION_NODE_STATE_MACHINE_H
+
+#include "scene/animation/animation_tree.h"
+
+class AnimationNodeStateMachineTransition : public Resource {
+ GDCLASS(AnimationNodeStateMachineTransition, Resource)
+public:
+ enum SwitchMode {
+ SWITCH_MODE_IMMEDIATE,
+ SWITCH_MODE_SYNC,
+ SWITCH_MODE_AT_END,
+ };
+
+private:
+ SwitchMode switch_mode;
+ bool auto_advance;
+ float xfade;
+ bool disabled;
+ int priority;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_switch_mode(SwitchMode p_mode);
+ SwitchMode get_switch_mode() const;
+
+ void set_auto_advance(bool p_enable);
+ bool has_auto_advance() const;
+
+ void set_xfade_time(float p_xfade);
+ float get_xfade_time() const;
+
+ void set_disabled(bool p_disabled);
+ bool is_disabled() const;
+
+ void set_priority(int p_priority);
+ int get_priority() const;
+
+ AnimationNodeStateMachineTransition();
+};
+
+VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode)
+
+class AnimationNodeStateMachine : public AnimationRootNode {
+
+ GDCLASS(AnimationNodeStateMachine, AnimationRootNode);
+
+private:
+ Map<StringName, Ref<AnimationRootNode> > states;
+
+ struct Transition {
+
+ StringName from;
+ StringName to;
+ Ref<AnimationNodeStateMachineTransition> transition;
+ };
+
+ struct AStarCost {
+ float distance;
+ StringName prev;
+ };
+
+ Vector<Transition> transitions;
+
+ float len_total;
+
+ float len_current;
+ float pos_current;
+ int loops_current;
+
+ bool play_start;
+ StringName start_node;
+ StringName end_node;
+
+ Vector2 graph_offset;
+
+ StringName current;
+
+ StringName fading_from;
+ float fading_time;
+ float fading_pos;
+
+ Vector<StringName> path;
+ bool playing;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ void add_node(const StringName &p_name, Ref<AnimationNode> p_node);
+ Ref<AnimationNode> get_node(const StringName &p_name) const;
+ void remove_node(const StringName &p_name);
+ void rename_node(const StringName &p_name, const StringName &p_new_name);
+ bool has_node(const StringName &p_name) const;
+ StringName get_node_name(const Ref<AnimationNode> &p_node) const;
+ void get_node_list(List<StringName> *r_nodes) const;
+
+ bool has_transition(const StringName &p_from, const StringName &p_to) const;
+ int find_transition(const StringName &p_from, const StringName &p_to) const;
+ void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition);
+ Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const;
+ StringName get_transition_from(int p_transition) const;
+ StringName get_transition_to(int p_transition) const;
+ int get_transition_count() const;
+ void remove_transition_by_index(int p_transition);
+ void remove_transition(const StringName &p_from, const StringName &p_to);
+
+ void set_start_node(const StringName &p_node);
+ String get_start_node() const;
+
+ void set_end_node(const StringName &p_node);
+ String get_end_node() const;
+
+ void set_graph_offset(const Vector2 &p_offset);
+ Vector2 get_graph_offset() const;
+
+ virtual float process(float p_time, bool p_seek);
+ virtual String get_caption() const;
+
+ bool travel(const StringName &p_state);
+ void start(const StringName &p_state);
+ void stop();
+ bool is_playing() const;
+ StringName get_current_node() const;
+ StringName get_blend_from_node() const;
+ Vector<StringName> get_travel_path() const;
+ float get_current_play_pos() const;
+ float get_current_length() const;
+
+ virtual void set_tree(AnimationTree *p_player);
+
+ AnimationNodeStateMachine();
+};
+
+#endif // ANIMATION_NODE_STATE_MACHINE_H
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 06aaeddf18..111620dac1 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -419,14 +419,26 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
pa->capture = pa->object->get_indexed(pa->subpath);
}
- if (a->track_get_key_count(i) == 0)
+ int key_count = a->track_get_key_count(i);
+ if (key_count == 0)
continue; //eeh not worth it
float first_key_time = a->track_get_key_time(i, 0);
+ float transition = 1.0;
+ int first_key = 0;
+
+ if (first_key_time == 0.0) {
+ //ignore, use for transition
+ if (key_count == 1)
+ continue; //with one key we cant do anything
+ transition = a->track_get_key_transition(i, 0);
+ first_key_time = a->track_get_key_time(i, 1);
+ first_key = 1;
+ }
if (p_time < first_key_time) {
- float c = p_time / first_key_time;
- Variant first_value = a->track_get_key_value(i, 0);
+ float c = Math::ease(p_time / first_key_time, transition);
+ Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
@@ -537,6 +549,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
int s = params.size();
ERR_CONTINUE(s > VARIANT_ARG_MAX);
+#ifdef DEBUG_ENABLED
+ if (!nc->node->has_method(method)) {
+ ERR_PRINTS("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'.");
+ }
+#endif
+
if (can_call) {
MessageQueue::get_singleton()->push_call(
nc->node,
@@ -648,7 +666,22 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- if (nc->audio_start > p_time || (nc->audio_len > 0 && p_time - nc->audio_start < nc->audio_len)) {
+
+ bool loop = a->has_loop();
+
+ bool stop = false;
+
+ if (!loop && p_time < nc->audio_start) {
+ stop = true;
+ } else if (nc->audio_len > 0) {
+ float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
+
+ if (len > nc->audio_len) {
+ stop = true;
+ }
+ }
+
+ if (stop) {
//time to stop
nc->node->call("stop");
nc->audio_playing = false;
diff --git a/scene/animation/animation_graph_player.cpp b/scene/animation/animation_tree.cpp
index b8efecebe9..4fa66e8ede 100644
--- a/scene/animation/animation_graph_player.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -1,4 +1,4 @@
-#include "animation_graph_player.h"
+#include "animation_tree.h"
#include "animation_blend_tree.h"
#include "core/method_bind_ext.gen.inc"
#include "engine.h"
@@ -56,11 +56,11 @@ void AnimationNode::make_invalid(const String &p_reason) {
float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
- ERR_FAIL_COND_V(!get_graph_player(), 0); //should not happen, but used to catch bugs
+ ERR_FAIL_COND_V(!get_tree(), 0); //should not happen, but used to catch bugs
Ref<AnimationNodeBlendTree> tree = get_parent();
- if (!tree.is_valid() && get_graph_player()->get_graph_root().ptr() != this) {
+ if (!tree.is_valid() && get_tree()->get_tree_root().ptr() != this) {
make_invalid(RTR("Can't blend input because node is not in a tree"));
return 0;
}
@@ -204,10 +204,10 @@ String AnimationNode::get_input_name(int p_input) {
float AnimationNode::get_input_activity(int p_input) const {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
- if (!get_graph_player())
+ if (!get_tree())
return 0;
- if (get_graph_player()->get_last_process_pass() != inputs[p_input].last_pass) {
+ if (get_tree()->get_last_process_pass() != inputs[p_input].last_pass) {
return 0;
}
return inputs[p_input].activity;
@@ -225,6 +225,11 @@ void AnimationNode::set_input_connection(int p_input, const StringName &p_connec
}
String AnimationNode::get_caption() const {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("get_caption");
+ }
+
return "Node";
}
@@ -253,8 +258,15 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
+void AnimationNode::_set_parent(Object *p_parent) {
+ set_parent(Object::cast_to<AnimationNode>(p_parent));
+}
+
void AnimationNode::set_parent(AnimationNode *p_parent) {
parent = p_parent; //do not use ref because parent contains children
+ if (get_script_instance()) {
+ get_script_instance()->call("_parent_set", p_parent);
+ }
}
Ref<AnimationNode> AnimationNode::get_parent() const {
@@ -265,7 +277,7 @@ Ref<AnimationNode> AnimationNode::get_parent() const {
return Ref<AnimationNode>();
}
-AnimationGraphPlayer *AnimationNode::get_graph_player() const {
+AnimationTree *AnimationNode::get_tree() const {
return player;
}
@@ -316,7 +328,7 @@ Vector2 AnimationNode::get_position() const {
return position;
}
-void AnimationNode::set_graph_player(AnimationGraphPlayer *p_player) {
+void AnimationNode::set_tree(AnimationTree *p_player) {
if (player != NULL && p_player == NULL) {
emit_signal("removed_from_graph");
@@ -375,10 +387,17 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("blend_node", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("set_parent", "parent"), &AnimationNode::_set_parent);
+ ClassDB::bind_method(D_METHOD("get_parent"), &AnimationNode::get_parent);
+ ClassDB::bind_method(D_METHOD("get_tree"), &AnimationNode::get_tree);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters");
BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek")));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption"));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter"));
+ BIND_VMETHOD(MethodInfo("_parent_set", PropertyInfo(Variant::OBJECT, "parent")));
ADD_SIGNAL(MethodInfo("removed_from_graph"));
BIND_ENUM_CONSTANT(FILTER_IGNORE);
@@ -398,10 +417,10 @@ AnimationNode::AnimationNode() {
////////////////////
-void AnimationGraphPlayer::set_graph_root(const Ref<AnimationNode> &p_root) {
+void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
if (root.is_valid()) {
- root->set_graph_player(NULL);
+ root->set_tree(NULL);
}
if (p_root.is_valid()) {
ERR_EXPLAIN("root node already set to another player");
@@ -410,17 +429,17 @@ void AnimationGraphPlayer::set_graph_root(const Ref<AnimationNode> &p_root) {
root = p_root;
if (root.is_valid()) {
- root->set_graph_player(this);
+ root->set_tree(this);
}
update_configuration_warning();
}
-Ref<AnimationNode> AnimationGraphPlayer::get_graph_root() const {
+Ref<AnimationNode> AnimationTree::get_tree_root() const {
return root;
}
-void AnimationGraphPlayer::set_active(bool p_active) {
+void AnimationTree::set_active(bool p_active) {
if (active == p_active)
return;
@@ -447,12 +466,12 @@ void AnimationGraphPlayer::set_active(bool p_active) {
}
}
-bool AnimationGraphPlayer::is_active() const {
+bool AnimationTree::is_active() const {
return active;
}
-void AnimationGraphPlayer::set_process_mode(AnimationProcessMode p_mode) {
+void AnimationTree::set_process_mode(AnimationProcessMode p_mode) {
if (process_mode == p_mode)
return;
@@ -469,20 +488,20 @@ void AnimationGraphPlayer::set_process_mode(AnimationProcessMode p_mode) {
}
}
-AnimationGraphPlayer::AnimationProcessMode AnimationGraphPlayer::get_process_mode() const {
+AnimationTree::AnimationProcessMode AnimationTree::get_process_mode() const {
return process_mode;
}
-void AnimationGraphPlayer::_node_removed(Node *p_node) {
+void AnimationTree::_node_removed(Node *p_node) {
cache_valid = false;
}
-bool AnimationGraphPlayer::_update_caches(AnimationPlayer *player) {
+bool AnimationTree::_update_caches(AnimationPlayer *player) {
setup_pass++;
if (!player->has_node(player->get_root())) {
- ERR_PRINT("AnimationGraphPlayer: AnimationPlayer root is invalid.");
+ ERR_PRINT("AnimationTree: AnimationPlayer root is invalid.");
set_active(false);
return false;
}
@@ -517,7 +536,7 @@ bool AnimationGraphPlayer::_update_caches(AnimationPlayer *player) {
Node *child = parent->get_node_and_resource(path, resource, leftover_path);
if (!child) {
- ERR_PRINTS("AnimationGraphPlayer: '" + String(E->get()) + "', couldn't resolve track: '" + String(path) + "'");
+ ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', couldn't resolve track: '" + String(path) + "'");
continue;
}
@@ -547,7 +566,7 @@ bool AnimationGraphPlayer::_update_caches(AnimationPlayer *player) {
Spatial *spatial = Object::cast_to<Spatial>(child);
if (!spatial) {
- ERR_PRINTS("AnimationGraphPlayer: '" + String(E->get()) + "', transform track does not point to spatial: '" + String(path) + "'");
+ ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', transform track does not point to spatial: '" + String(path) + "'");
continue;
}
@@ -666,7 +685,7 @@ bool AnimationGraphPlayer::_update_caches(AnimationPlayer *player) {
return true;
}
-void AnimationGraphPlayer::_clear_caches() {
+void AnimationTree::_clear_caches() {
const NodePath *K = NULL;
while ((K = track_cache.next(K))) {
@@ -678,19 +697,20 @@ void AnimationGraphPlayer::_clear_caches() {
cache_valid = false;
}
-void AnimationGraphPlayer::_process_graph(float p_delta) {
+void AnimationTree::_process_graph(float p_delta) {
//check all tracks, see if they need modification
+ root_motion_transform = Transform();
if (!root.is_valid()) {
- ERR_PRINT("AnimationGraphPlayer: root AnimationNode is not set, disabling playback.");
+ ERR_PRINT("AnimationTree: root AnimationNode is not set, disabling playback.");
set_active(false);
cache_valid = false;
return;
}
if (!has_node(animation_player)) {
- ERR_PRINT("AnimationGraphPlayer: no valid AnimationPlayer path set, disabling playback");
+ ERR_PRINT("AnimationTree: no valid AnimationPlayer path set, disabling playback");
set_active(false);
cache_valid = false;
return;
@@ -699,7 +719,7 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
if (!player) {
- ERR_PRINT("AnimationGraphPlayer: path points to a node not an AnimationPlayer, disabling playback");
+ ERR_PRINT("AnimationTree: path points to a node not an AnimationPlayer, disabling playback");
set_active(false);
cache_valid = false;
return;
@@ -770,6 +790,8 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
continue; //may happen should not
}
+ track->root_motion = root_motion_track == path;
+
ERR_CONTINUE(!state.track_map.has(path));
int blend_idx = state.track_map[path];
@@ -786,29 +808,85 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- Vector3 loc;
- Quat rot;
- Vector3 scale;
-
- Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
- scale -= Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes
-
- if (err != OK)
- continue;
-
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3();
t->rot = Quat();
+ t->rot_blend_accum = 0;
t->scale = Vector3();
}
- t->loc = t->loc.linear_interpolate(loc, blend);
- t->rot = t->rot.slerp(rot, blend);
- t->scale = t->scale.linear_interpolate(scale, blend);
+ if (track->root_motion) {
+
+ float prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
+ }
+
+ Vector3 loc[2];
+ Quat rot[2];
+ Vector3 scale[2];
+
+ if (prev_time > time) {
+
+ Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ t->scale += (scale[1] - scale[0]) * blend;
+ Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+
+ prev_time = 0;
+ }
+
+ Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ t->scale += (scale[1] - scale[0]) * blend;
+ Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+
+ prev_time = 0;
+
+ } else {
+ Vector3 loc;
+ Quat rot;
+ Vector3 scale;
+
+ Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ scale -= Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes
+
+ if (err != OK)
+ continue;
+
+ t->loc = t->loc.linear_interpolate(loc, blend);
+ if (t->rot_blend_accum == 0) {
+ t->rot = rot;
+ t->rot_blend_accum = blend;
+ } else {
+ float rot_total = t->rot_blend_accum + blend;
+ t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
+ t->rot_blend_accum = rot_total;
+ }
+ t->scale = t->scale.linear_interpolate(scale, blend);
+ }
} break;
case Animation::TYPE_VALUE: {
@@ -963,7 +1041,22 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
t->start = time;
}
} else if (t->playing) {
- if (t->start > time || (t->len > 0 && time - t->start < t->len)) {
+
+ bool loop = a->has_loop();
+
+ bool stop = false;
+
+ if (!loop && time < t->start) {
+ stop = true;
+ } else if (t->len > 0) {
+ float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
+
+ if (len > t->len) {
+ stop = true;
+ }
+ }
+
+ if (stop) {
//time to stop
t->object->call("stop");
t->playing = false;
@@ -972,6 +1065,12 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
}
}
+ float db = Math::linear2db(MAX(blend, 0.00001));
+ if (t->object->has_method("set_unit_db")) {
+ t->object->call("set_unit_db", db);
+ } else {
+ t->object->call("set_volume_db", db);
+ }
} break;
case Animation::TYPE_ANIMATION: {
@@ -1059,11 +1158,18 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
Transform xform;
xform.origin = t->loc;
- t->scale += Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes
+ t->scale += Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes and root motion
xform.basis.set_quat_scale(t->rot, t->scale);
- if (t->skeleton && t->bone_idx >= 0) {
+ if (t->root_motion) {
+
+ root_motion_transform = xform;
+
+ if (t->skeleton && t->bone_idx >= 0) {
+ root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
+ }
+ } else if (t->skeleton && t->bone_idx >= 0) {
t->skeleton->set_bone_pose(t->bone_idx, xform);
@@ -1093,7 +1199,7 @@ void AnimationGraphPlayer::_process_graph(float p_delta) {
}
}
-void AnimationGraphPlayer::_notification(int p_what) {
+void AnimationTree::_notification(int p_what) {
if (active && p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS && process_mode == ANIMATION_PROCESS_PHYSICS) {
_process_graph(get_physics_process_delta_time());
@@ -1108,29 +1214,29 @@ void AnimationGraphPlayer::_notification(int p_what) {
}
}
-void AnimationGraphPlayer::set_animation_player(const NodePath &p_player) {
+void AnimationTree::set_animation_player(const NodePath &p_player) {
animation_player = p_player;
update_configuration_warning();
}
-NodePath AnimationGraphPlayer::get_animation_player() const {
+NodePath AnimationTree::get_animation_player() const {
return animation_player;
}
-bool AnimationGraphPlayer::is_state_invalid() const {
+bool AnimationTree::is_state_invalid() const {
return !state.valid;
}
-String AnimationGraphPlayer::get_invalid_state_reason() const {
+String AnimationTree::get_invalid_state_reason() const {
return state.invalid_reasons;
}
-uint64_t AnimationGraphPlayer::get_last_process_pass() const {
+uint64_t AnimationTree::get_last_process_pass() const {
return process_pass;
}
-String AnimationGraphPlayer::get_configuration_warning() const {
+String AnimationTree::get_configuration_warning() const {
String warning = Node::get_configuration_warning();
@@ -1174,28 +1280,50 @@ String AnimationGraphPlayer::get_configuration_warning() const {
return warning;
}
-void AnimationGraphPlayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationGraphPlayer::set_active);
- ClassDB::bind_method(D_METHOD("is_active"), &AnimationGraphPlayer::is_active);
+void AnimationTree::set_root_motion_track(const NodePath &p_track) {
+ root_motion_track = p_track;
+}
+
+NodePath AnimationTree::get_root_motion_track() const {
+ return root_motion_track;
+}
+
+Transform AnimationTree::get_root_motion_transform() const {
+ return root_motion_transform;
+}
+
+void AnimationTree::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationTree::set_active);
+ ClassDB::bind_method(D_METHOD("is_active"), &AnimationTree::is_active);
+
+ ClassDB::bind_method(D_METHOD("set_tree_root", "root"), &AnimationTree::set_tree_root);
+ ClassDB::bind_method(D_METHOD("get_tree_root"), &AnimationTree::get_tree_root);
- ClassDB::bind_method(D_METHOD("set_graph_root", "root"), &AnimationGraphPlayer::set_graph_root);
- ClassDB::bind_method(D_METHOD("get_graph_root"), &AnimationGraphPlayer::get_graph_root);
+ ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &AnimationTree::set_process_mode);
+ ClassDB::bind_method(D_METHOD("get_process_mode"), &AnimationTree::get_process_mode);
- ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &AnimationGraphPlayer::set_process_mode);
- ClassDB::bind_method(D_METHOD("get_process_mode"), &AnimationGraphPlayer::get_process_mode);
+ ClassDB::bind_method(D_METHOD("set_animation_player", "root"), &AnimationTree::set_animation_player);
+ ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationTree::get_animation_player);
- ClassDB::bind_method(D_METHOD("set_animation_player", "root"), &AnimationGraphPlayer::set_animation_player);
- ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationGraphPlayer::get_animation_player);
+ ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
+ ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
- ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationGraphPlayer::_node_removed);
+ ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "graph_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_graph_root", "get_graph_root");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player"), "set_animation_player", "get_animation_player");
+ ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode");
+ ADD_GROUP("Root Motion", "root_motion_");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
+
+ BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
}
-AnimationGraphPlayer::AnimationGraphPlayer() {
+AnimationTree::AnimationTree() {
process_mode = ANIMATION_PROCESS_IDLE;
active = false;
@@ -1204,7 +1332,7 @@ AnimationGraphPlayer::AnimationGraphPlayer() {
started = true;
}
-AnimationGraphPlayer::~AnimationGraphPlayer() {
+AnimationTree::~AnimationTree() {
if (root.is_valid()) {
root->player = NULL;
}
diff --git a/scene/animation/animation_graph_player.h b/scene/animation/animation_tree.h
index 8952be75c9..540c36437a 100644
--- a/scene/animation/animation_graph_player.h
+++ b/scene/animation/animation_tree.h
@@ -8,7 +8,7 @@
class AnimationNodeBlendTree;
class AnimationPlayer;
-class AnimationGraphPlayer;
+class AnimationTree;
class AnimationNode : public Resource {
GDCLASS(AnimationNode, Resource)
@@ -32,7 +32,7 @@ public:
float process_input(int p_input, float p_time, bool p_seek, float p_blend);
- friend class AnimationGraphPlayer;
+ friend class AnimationTree;
struct AnimationState {
@@ -62,7 +62,7 @@ public:
Vector2 position;
AnimationNode *parent;
- AnimationGraphPlayer *player;
+ AnimationTree *player;
float _blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL);
@@ -82,11 +82,13 @@ protected:
void _validate_property(PropertyInfo &property) const;
+ void _set_parent(Object *p_parent);
+
public:
void set_parent(AnimationNode *p_parent);
Ref<AnimationNode> get_parent() const;
- virtual void set_graph_player(AnimationGraphPlayer *p_player);
- AnimationGraphPlayer *get_graph_player() const;
+ virtual void set_tree(AnimationTree *p_player);
+ AnimationTree *get_tree() const;
AnimationPlayer *get_player() const;
virtual float process(float p_time, bool p_seek);
@@ -125,8 +127,8 @@ public:
AnimationRootNode() {}
};
-class AnimationGraphPlayer : public Node {
- GDCLASS(AnimationGraphPlayer, Node)
+class AnimationTree : public Node {
+ GDCLASS(AnimationTree, Node)
public:
enum AnimationProcessMode {
ANIMATION_PROCESS_PHYSICS,
@@ -135,6 +137,8 @@ public:
private:
struct TrackCache {
+
+ bool root_motion;
uint64_t setup_pass;
uint64_t process_pass;
Animation::TrackType type;
@@ -142,6 +146,7 @@ private:
ObjectID object_id;
TrackCache() {
+ root_motion = false;
setup_pass = 0;
process_pass = 0;
object = NULL;
@@ -156,6 +161,7 @@ private:
int bone_idx;
Vector3 loc;
Quat rot;
+ float rot_blend_accum;
Vector3 scale;
TrackCacheTransform() {
@@ -235,13 +241,16 @@ private:
bool started;
+ NodePath root_motion_track;
+ Transform root_motion_transform;
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void set_graph_root(const Ref<AnimationNode> &p_root);
- Ref<AnimationNode> get_graph_root() const;
+ void set_tree_root(const Ref<AnimationNode> &p_root);
+ Ref<AnimationNode> get_tree_root() const;
void set_active(bool p_active);
bool is_active() const;
@@ -257,11 +266,16 @@ public:
bool is_state_invalid() const;
String get_invalid_state_reason() const;
+ void set_root_motion_track(const NodePath &p_track);
+ NodePath get_root_motion_track() const;
+
+ Transform get_root_motion_transform() const;
+
uint64_t get_last_process_pass() const;
- AnimationGraphPlayer();
- ~AnimationGraphPlayer();
+ AnimationTree();
+ ~AnimationTree();
};
-VARIANT_ENUM_CAST(AnimationGraphPlayer::AnimationProcessMode)
+VARIANT_ENUM_CAST(AnimationTree::AnimationProcessMode)
#endif // ANIMATION_GRAPH_PLAYER_H
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 143684bdf9..026215508b 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -1814,7 +1814,7 @@ void AnimationTreePlayer::_bind_methods() {
ADD_GROUP("Playback", "playback_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_animation_process_mode", "get_animation_process_mode");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player"), "set_master_player", "get_master_player");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_master_player", "get_master_player");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "base_path"), "set_base_path", "get_base_path");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
new file mode 100644
index 0000000000..a28c63064a
--- /dev/null
+++ b/scene/animation/root_motion_view.cpp
@@ -0,0 +1,178 @@
+#include "root_motion_view.h"
+#include "scene/animation/animation_tree.h"
+#include "scene/resources/material.h"
+void RootMotionView::set_animation_path(const NodePath &p_path) {
+ path = p_path;
+ first = true;
+}
+
+NodePath RootMotionView::get_animation_path() const {
+ return path;
+}
+
+void RootMotionView::set_color(const Color &p_color) {
+ color = p_color;
+ first = true;
+}
+
+Color RootMotionView::get_color() const {
+ return color;
+}
+
+void RootMotionView::set_cell_size(float p_size) {
+ cell_size = p_size;
+ first = true;
+}
+
+float RootMotionView::get_cell_size() const {
+ return cell_size;
+}
+
+void RootMotionView::set_radius(float p_radius) {
+ radius = p_radius;
+ first = true;
+}
+
+float RootMotionView::get_radius() const {
+ return radius;
+}
+
+void RootMotionView::set_zero_y(bool p_zero_y) {
+ zero_y = p_zero_y;
+}
+
+bool RootMotionView::get_zero_y() const {
+ return zero_y;
+}
+
+void RootMotionView::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ VS::get_singleton()->immediate_set_material(immediate, SpatialMaterial::get_material_rid_for_2d(false, true, false, false, false));
+ first = true;
+ }
+
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+ Transform transform;
+
+ if (has_node(path)) {
+
+ Node *node = get_node(path);
+
+ AnimationTree *tree = Object::cast_to<AnimationTree>(node);
+ if (tree && tree->is_active() && tree->get_root_motion_track() != NodePath()) {
+ if (is_processing_internal() && tree->get_process_mode() == AnimationTree::ANIMATION_PROCESS_PHYSICS) {
+ set_process_internal(false);
+ set_physics_process_internal(true);
+ }
+
+ if (is_physics_processing_internal() && tree->get_process_mode() == AnimationTree::ANIMATION_PROCESS_IDLE) {
+ set_process_internal(true);
+ set_physics_process_internal(false);
+ }
+
+ transform = tree->get_root_motion_transform();
+ }
+ }
+
+ if (!first && transform == Transform()) {
+ return;
+ }
+
+ first = false;
+
+ transform.orthonormalize(); //dont want scale, too imprecise
+ transform.affine_invert();
+
+ accumulated = transform * accumulated;
+ accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
+ if (zero_y) {
+ accumulated.origin.y = 0;
+ }
+ accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size);
+
+ VS::get_singleton()->immediate_clear(immediate);
+
+ int cells_in_radius = int((radius / cell_size) + 1.0);
+
+ VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_LINES);
+ for (int i = -cells_in_radius; i < cells_in_radius; i++) {
+ for (int j = -cells_in_radius; j < cells_in_radius; j++) {
+
+ Vector3 from(i * cell_size, 0, j * cell_size);
+ Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
+ Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
+ from = accumulated.xform(from);
+ from_i = accumulated.xform(from_i);
+ from_j = accumulated.xform(from_j);
+
+ Color c = color, c_i = color, c_j = color;
+ c.a *= MAX(0, 1.0 - from.length() / radius);
+ c_i.a *= MAX(0, 1.0 - from_i.length() / radius);
+ c_j.a *= MAX(0, 1.0 - from_j.length() / radius);
+
+ VS::get_singleton()->immediate_color(immediate, c);
+ VS::get_singleton()->immediate_vertex(immediate, from);
+
+ VS::get_singleton()->immediate_color(immediate, c_i);
+ VS::get_singleton()->immediate_vertex(immediate, from_i);
+
+ VS::get_singleton()->immediate_color(immediate, c);
+ VS::get_singleton()->immediate_vertex(immediate, from);
+
+ VS::get_singleton()->immediate_color(immediate, c_j);
+ VS::get_singleton()->immediate_vertex(immediate, from_j);
+ }
+ }
+
+ VS::get_singleton()->immediate_end(immediate);
+ }
+}
+
+AABB RootMotionView::get_aabb() const {
+
+ return AABB(Vector3(-radius, 0, -radius), Vector3(radius * 2, 0.001, radius * 2));
+}
+PoolVector<Face3> RootMotionView::get_faces(uint32_t p_usage_flags) const {
+ return PoolVector<Face3>();
+}
+
+void RootMotionView::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_animation_path", "path"), &RootMotionView::set_animation_path);
+ ClassDB::bind_method(D_METHOD("get_animation_path"), &RootMotionView::get_animation_path);
+
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &RootMotionView::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &RootMotionView::get_color);
+
+ ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &RootMotionView::set_cell_size);
+ ClassDB::bind_method(D_METHOD("get_cell_size"), &RootMotionView::get_cell_size);
+
+ ClassDB::bind_method(D_METHOD("set_radius", "size"), &RootMotionView::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &RootMotionView::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_zero_y", "enable"), &RootMotionView::set_zero_y);
+ ClassDB::bind_method(D_METHOD("get_zero_y"), &RootMotionView::get_zero_y);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationTree"), "set_animation_path", "get_animation_path");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_y"), "set_zero_y", "get_zero_y");
+}
+
+RootMotionView::RootMotionView() {
+ zero_y = true;
+ radius = 10;
+ cell_size = 1;
+ set_process_internal(true);
+ immediate = VisualServer::get_singleton()->immediate_create();
+ set_base(immediate);
+ color = Color(0.5, 0.5, 1.0);
+}
+
+RootMotionView::~RootMotionView() {
+ set_base(RID());
+ VisualServer::get_singleton()->free(immediate);
+}
diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h
new file mode 100644
index 0000000000..611183d364
--- /dev/null
+++ b/scene/animation/root_motion_view.h
@@ -0,0 +1,47 @@
+#ifndef ROOT_MOTION_VIEW_H
+#define ROOT_MOTION_VIEW_H
+
+#include "scene/3d/visual_instance.h"
+
+class RootMotionView : public VisualInstance {
+ GDCLASS(RootMotionView, VisualInstance)
+public:
+ RID immediate;
+ NodePath path;
+ float cell_size;
+ float radius;
+ bool use_in_game;
+ Color color;
+ bool first;
+ bool zero_y;
+
+ Transform accumulated;
+
+private:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_animation_path(const NodePath &p_path);
+ NodePath get_animation_path() const;
+
+ void set_color(const Color &p_path);
+ Color get_color() const;
+
+ void set_cell_size(float p_size);
+ float get_cell_size() const;
+
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ void set_zero_y(bool p_zero_y);
+ bool get_zero_y() const;
+
+ virtual AABB get_aabb() const;
+ virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ RootMotionView();
+ ~RootMotionView();
+};
+
+#endif // ROOT_MOTION_VIEW_H
diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp
index 408c00334a..863b278b62 100644
--- a/scene/audio/audio_player.cpp
+++ b/scene/audio/audio_player.cpp
@@ -41,10 +41,10 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
int buffer_size = mix_buffer.size();
if (p_fadeout) {
- buffer_size = MIN(buffer_size, 16); //short fadeout ramp
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
}
- //mix
stream_playback->mix(buffer, pitch_scale, buffer_size);
//multiply volume interpolating to avoid clicks if this changes
@@ -56,6 +56,7 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
buffer[i] *= vol;
vol += vol_inc;
}
+
//set volume for next mix
mix_volume_db = target_volume;
@@ -90,11 +91,14 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
void AudioStreamPlayer::_mix_audio() {
- if (!stream_playback.is_valid()) {
+ if (!stream_playback.is_valid() || !active)
return;
- }
- if (!active) {
+ if (stream_paused) {
+ if (stream_paused_fade) {
+ _mix_internal(true);
+ stream_paused_fade = false;
+ }
return;
}
@@ -135,6 +139,17 @@ void AudioStreamPlayer::_notification(int p_what) {
AudioServer::get_singleton()->remove_callback(_mix_audios, this);
}
+
+ if (p_what == NOTIFICATION_PAUSED) {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ }
+
+ if (p_what == NOTIFICATION_UNPAUSED) {
+ set_stream_paused(false);
+ }
}
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
@@ -159,7 +174,6 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
- ERR_FAIL_COND(stream_playback.is_null());
}
}
@@ -275,6 +289,19 @@ bool AudioStreamPlayer::_is_active() const {
return active;
}
+void AudioStreamPlayer::set_stream_paused(bool p_pause) {
+
+ if (p_pause != stream_paused) {
+ stream_paused = p_pause;
+ stream_paused_fade = p_pause ? true : false;
+ }
+}
+
+bool AudioStreamPlayer::get_stream_paused() const {
+
+ return stream_paused;
+}
+
void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
if (property.name == "bus") {
@@ -328,11 +355,15 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed);
+ ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
@@ -351,6 +382,8 @@ AudioStreamPlayer::AudioStreamPlayer() {
autoplay = false;
setseek = -1;
active = false;
+ stream_paused = false;
+ stream_paused_fade = false;
mix_target = MIX_TARGET_STEREO;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h
index 21189aea6d..591c00ed18 100644
--- a/scene/audio/audio_player.h
+++ b/scene/audio/audio_player.h
@@ -57,6 +57,8 @@ private:
float pitch_scale;
float volume_db;
bool autoplay;
+ bool stream_paused;
+ bool stream_paused_fade;
StringName bus;
MixTarget mix_target;
@@ -100,6 +102,9 @@ public:
void set_mix_target(MixTarget p_target);
MixTarget get_mix_target() const;
+ void set_stream_paused(bool p_pause);
+ bool get_stream_paused() const;
+
AudioStreamPlayer();
~AudioStreamPlayer();
};
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 34891832e2..8e232c6f46 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -242,6 +242,14 @@ bool ColorPicker::is_raw_mode() const {
return raw_mode_enabled;
}
+void ColorPicker::set_deferred_mode(bool p_enabled) {
+ deferred_mode_enabled = p_enabled;
+}
+
+bool ColorPicker::is_deferred_mode() const {
+ return deferred_mode_enabled;
+}
+
void ColorPicker::_update_text_value() {
bool visible = true;
if (text_is_constructor) {
@@ -328,7 +336,11 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
emit_signal("color_changed", color);
+ changing_color = false;
} else {
changing_color = false;
}
@@ -347,7 +359,8 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -368,7 +381,10 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ else if (!bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT)
+ emit_signal("color_changed", color);
}
Ref<InputEventMouseMotion> mev = p_event;
@@ -383,7 +399,8 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -500,6 +517,8 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
ClassDB::bind_method(D_METHOD("set_raw_mode", "mode"), &ColorPicker::set_raw_mode);
ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
+ ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
+ ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
@@ -522,6 +541,7 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
}
@@ -533,6 +553,7 @@ ColorPicker::ColorPicker() :
edit_alpha = true;
text_is_constructor = false;
raw_mode_enabled = false;
+ deferred_mode_enabled = false;
changing_color = false;
screen = NULL;
@@ -722,8 +743,9 @@ ColorPicker *ColorPickerButton::get_picker() {
return picker;
}
-PopupPanel *ColorPickerButton::get_popup() const {
+PopupPanel *ColorPickerButton::get_popup() {
+ _update_picker();
return popup;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 6b63e5fe60..0166da7118 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -67,6 +67,7 @@ private:
Color color;
bool raw_mode_enabled;
+ bool deferred_mode_enabled;
bool updating;
bool changing_color;
float h, s, v;
@@ -107,6 +108,9 @@ public:
void set_raw_mode(bool p_enabled);
bool is_raw_mode() const;
+ void set_deferred_mode(bool p_enabled);
+ bool is_deferred_mode() const;
+
void set_focus_on_line_edit();
ColorPicker();
@@ -140,7 +144,7 @@ public:
bool is_editing_alpha() const;
ColorPicker *get_picker();
- PopupPanel *get_popup() const;
+ PopupPanel *get_popup();
ColorPickerButton();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a738687a70..17c349858f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -160,9 +160,16 @@ void Control::_update_minimum_size_cache() {
Size2 minsize = get_minimum_size();
minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize)
+ size_changed = true;
+
data.minimum_size_cache = minsize;
data.minimum_size_valid = true;
- minimum_size_changed();
+
+ if (size_changed)
+ minimum_size_changed();
}
Size2 Control::get_combined_minimum_size() const {
@@ -1267,35 +1274,34 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
return Theme::get_default()->has_constant(p_name, type);
}
-Size2 Control::get_parent_area_size() const {
-
- ERR_FAIL_COND_V(!is_inside_tree(), Size2());
-
- Size2 parent_size;
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree())
+ return Rect2();
+ Rect2 parent_rect;
if (data.parent_canvas_item) {
-
- parent_size = data.parent_canvas_item->_edit_get_rect().size;
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
-
- parent_size = get_viewport()->get_visible_rect().size;
+ parent_rect = get_viewport()->get_visible_rect();
}
- return parent_size;
+ return parent_rect;
}
-void Control::_size_changed() {
+Size2 Control::get_parent_area_size() const {
- if (!is_inside_tree())
- return;
+ return get_parent_anchorable_rect().size;
+}
+
+void Control::_size_changed() {
- Size2 parent_size = get_parent_area_size();
+ Rect2 parent_rect = get_parent_anchorable_rect();
float margin_pos[4];
for (int i = 0; i < 4; i++) {
- float area = parent_size[i & 1];
+ float area = parent_rect.size[i & 1];
margin_pos[i] = data.margin[i] + (data.anchor[i] * area);
}
@@ -1325,9 +1331,9 @@ void Control::_size_changed() {
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
- if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
- new_size_cache = new_size_cache.floor();
- new_pos_cache = new_pos_cache.floor();
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ new_size_cache = new_size_cache.round();
+ new_pos_cache = new_pos_cache.round();
}
bool pos_changed = new_pos_cache != data.pos_cache;
bool size_changed = new_size_cache != data.size_cache;
@@ -1335,57 +1341,25 @@ void Control::_size_changed() {
data.pos_cache = new_pos_cache;
data.size_cache = new_size_cache;
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _change_notify_margins();
- _notify_transform();
- }
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _change_notify_margins();
+ _notify_transform();
+ }
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
}
}
-float Control::_get_parent_range(int p_idx) const {
-
- if (!is_inside_tree()) {
-
- return 0;
- }
- if (data.parent_canvas_item) {
-
- return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1];
- } else {
- return get_viewport()->get_visible_rect().size[p_idx & 1];
- }
-
- return 0;
-}
-
-float Control::_get_range(int p_idx) const {
-
- p_idx &= 1;
-
- float parent_range = _get_parent_range(p_idx);
- float from = _a2s(data.margin[p_idx], data.anchor[p_idx], parent_range);
- float to = _a2s(data.margin[p_idx + 2], data.anchor[p_idx + 2], parent_range);
-
- return to - from;
-}
-
-float Control::_s2a(float p_val, float p_anchor, float p_range) const {
- return p_val - (p_anchor * p_range);
-}
-
-float Control::_a2s(float p_val, float p_anchor, float p_range) const {
- return Math::floor(p_val + (p_anchor * p_range));
-}
-
void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) {
- float parent_range = _get_parent_range((p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? 0 : 1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ float parent_range = (p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
@@ -1401,9 +1375,9 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
if (!p_keep_margin) {
- data.margin[p_margin] = _s2a(previous_margin_pos, data.anchor[p_margin], parent_range);
+ data.margin[p_margin] = previous_margin_pos - data.anchor[p_margin] * parent_range;
if (p_push_opposite_anchor) {
- data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range);
+ data.margin[(p_margin + 2) % 4] = previous_opposite_margin_pos - data.anchor[(p_margin + 2) % 4] * parent_range;
}
}
if (is_inside_tree()) {
@@ -1411,7 +1385,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
update();
- _change_notify();
+ _change_notify("anchor");
}
void Control::_set_anchor(Margin p_margin, float p_anchor) {
@@ -1557,8 +1531,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
new_size.y = min_size.y;
}
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
//Left
switch (p_preset) {
@@ -1570,21 +1543,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[0] = pw * (0.0 - data.anchor[0]) + p_margin;
+ data.margin[0] = parent_rect.size.x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[0] = pw * (0.5 - data.anchor[0]) - new_size.x / 2;
+ data.margin[0] = parent_rect.size.x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- data.margin[0] = pw * (1.0 - data.anchor[0]) - new_size.x - p_margin;
+ data.margin[0] = parent_rect.size.x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x;
break;
}
@@ -1598,21 +1571,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[1] = ph * (0.0 - data.anchor[1]) + p_margin;
+ data.margin[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[1] = ph * (0.5 - data.anchor[1]) - new_size.y / 2;
+ data.margin[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- data.margin[1] = ph * (1.0 - data.anchor[1]) - new_size.y - p_margin;
+ data.margin[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y;
break;
}
@@ -1622,14 +1595,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- data.margin[2] = pw * (0.0 - data.anchor[2]) + new_size.x + p_margin;
+ data.margin[2] = parent_rect.size.x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[2] = pw * (0.5 - data.anchor[2]) + new_size.x / 2;
+ data.margin[2] = parent_rect.size.x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
@@ -1640,7 +1613,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[2] = pw * (1.0 - data.anchor[2]) - p_margin;
+ data.margin[2] = parent_rect.size.x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1650,14 +1623,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- data.margin[3] = ph * (0.0 - data.anchor[3]) + new_size.y + p_margin;
+ data.margin[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[3] = ph * (0.5 - data.anchor[3]) + new_size.y / 2;
+ data.margin[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
@@ -1668,7 +1641,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[3] = ph * (1.0 - data.anchor[3]) - p_margin;
+ data.margin[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y;
break;
}
@@ -1747,31 +1720,29 @@ void Control::set_global_position(const Point2 &p_point) {
set_position(inv.xform(p_point));
}
-void Control::set_position(const Size2 &p_point) {
-
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
- float x2 = _a2s(data.margin[2], data.anchor[2], pw);
- float y2 = _a2s(data.margin[3], data.anchor[3], ph);
+ Rect2 anchorable = get_parent_anchorable_rect();
+ Rect2 result = anchorable;
+ for (int i = 0; i < 4; i++) {
+ result.grow_margin((Margin)i, p_anchors[i] * anchorable.get_size()[i % 2] + p_margins[i]);
+ }
- Size2 ret = Size2(x2 - x, y2 - y);
- Size2 min = get_combined_minimum_size();
+ return result;
+}
- Size2 size = Size2(MAX(min.width, ret.width), MAX(min.height, ret.height));
- float w = size.x;
- float h = size.y;
+void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
- x = p_point.x;
- y = p_point.y;
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ r_margins[0] = Math::floor(p_rect.position.x - (p_anchors[0] * parent_rect_size.x));
+ r_margins[1] = Math::floor(p_rect.position.y - (p_anchors[1] * parent_rect_size.y));
+ r_margins[2] = Math::floor(p_rect.position.x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x));
+ r_margins[3] = Math::floor(p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y));
+}
- data.margin[0] = _s2a(x, data.anchor[0], pw);
- data.margin[1] = _s2a(y, data.anchor[1], ph);
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
+void Control::set_position(const Size2 &p_point) {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
_size_changed();
}
@@ -1784,18 +1755,7 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
-
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
-
- float w = new_size.width;
- float h = new_size.height;
-
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
-
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
_size_changed();
}
@@ -1826,6 +1786,11 @@ Rect2 Control::get_rect() const {
return Rect2(get_position(), get_size());
}
+Rect2 Control::get_anchorable_rect() const {
+
+ return Rect2(Point2(), get_size());
+}
+
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
ERR_FAIL_COND(p_icon.is_null());
@@ -2223,10 +2188,17 @@ void Control::set_tooltip(const String &p_tooltip) {
data.tooltip = p_tooltip;
}
+
String Control::get_tooltip(const Point2 &p_pos) const {
return data.tooltip;
}
+Control *Control::make_custom_tooltip(const String &p_text) const {
+ if (get_script_instance()) {
+ return const_cast<Control *>(this)->call("_make_custom_tooltip", p_text);
+ }
+ return NULL;
+}
void Control::set_default_cursor_shape(CursorShape p_shape) {
@@ -2325,12 +2297,11 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
Point2 points[4];
Transform2D xform = get_global_transform();
- Rect2 rect = _edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
const Vector2 dir[4] = {
Vector2(-1, 0),
@@ -2384,12 +2355,11 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
Point2 points[4];
Transform2D xform = c->get_global_transform();
- Rect2 rect = c->_edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
float min = 1e7;
@@ -2857,6 +2827,7 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::OBJECT, "get_drag_data", PropertyInfo(Variant::VECTOR2, "position")));
BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
+ BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text")));
ADD_GROUP("Anchor", "anchor_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT);
@@ -2888,12 +2859,12 @@ void Control::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
ADD_GROUP("Focus", "focus_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next"), "set_focus_next", "get_focus_next");
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous"), "set_focus_previous", "get_focus_previous");
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_GROUP("Mouse", "mouse_");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 9124256624..94231867d7 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -202,12 +202,12 @@ private:
NodePath focus_next;
NodePath focus_prev;
- HashMap<StringName, Ref<Texture>, StringNameHasher> icon_override;
- HashMap<StringName, Ref<Shader>, StringNameHasher> shader_override;
- HashMap<StringName, Ref<StyleBox>, StringNameHasher> style_override;
- HashMap<StringName, Ref<Font>, StringNameHasher> font_override;
- HashMap<StringName, Color, StringNameHasher> color_override;
- HashMap<StringName, int, StringNameHasher> constant_override;
+ HashMap<StringName, Ref<Texture> > icon_override;
+ HashMap<StringName, Ref<Shader> > shader_override;
+ HashMap<StringName, Ref<StyleBox> > style_override;
+ HashMap<StringName, Ref<Font> > font_override;
+ HashMap<StringName, Color> color_override;
+ HashMap<StringName, int> constant_override;
Map<Ref<Font>, int> font_refcount;
} data;
@@ -220,10 +220,6 @@ private:
void _set_anchor(Margin p_margin, float p_anchor);
- float _get_parent_range(int p_idx) const;
- float _get_range(int p_idx) const;
- float _s2a(float p_val, float p_anchor, float p_range) const;
- float _a2s(float p_val, float p_anchor, float p_range) const;
void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -233,6 +229,9 @@ private:
void _update_scroll();
void _resize(const Size2 &p_size);
+ Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
+ void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+
void _size_changed();
String _get_tooltip() const;
@@ -283,6 +282,7 @@ public:
};
+ /* EDITOR */
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -358,6 +358,7 @@ public:
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
+ Rect2 get_anchorable_rect() const;
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
@@ -453,6 +454,7 @@ public:
void set_tooltip(const String &p_tooltip);
virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual Control *make_custom_tooltip(const String &p_text) const;
/* CURSOR */
@@ -465,6 +467,7 @@ public:
bool is_toplevel_control() const;
Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
void grab_click_focus();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 57b9a9a11b..aa52739b0a 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -285,7 +285,7 @@ void ItemList::unselect_all() {
items[i].selected = false;
}
-
+ current = -1;
update();
}
@@ -942,6 +942,7 @@ void ItemList::_notification(int p_what) {
}
}
+ minimum_size_changed();
shape_changed = false;
}
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 9af479c1cc..0b36e1663c 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -75,7 +75,7 @@ void Label::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
- if (clip || autowrap) {
+ if (clip) {
VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 0cd5219f8f..b71a4dd133 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -711,7 +711,8 @@ void LineEdit::_notification(int p_what) {
if (selected)
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
- drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
+ int yofs = y_ofs + (caret_height - font->get_height()) / 2;
+ drawer.draw_char(ci, Point2(x_ofs, yofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
if (char_ofs == cursor_pos && draw_caret) {
if (ime_text.length() == 0) {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index c5e4149782..2901176a69 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -36,7 +36,7 @@ Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
if (has_icon("arrow"))
- minsize.width += Control::get_icon("arrow")->get_width();
+ minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation");
return minsize;
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 18e609c798..ebec61ee6d 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -398,6 +398,15 @@ void PopupMenu::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
+ if (pm) {
+ // Inherit submenu's popup delay time from parent menu
+ float pm_delay = pm->get_submenu_popup_delay();
+ set_submenu_popup_delay(pm_delay);
+ }
+ } break;
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < items.size(); i++) {
@@ -421,6 +430,8 @@ void PopupMenu::_notification(int p_what) {
Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
+ Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left");
+ Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right");
style->draw(ci, Rect2(Point2(), get_size()));
Point2 ofs = style->get_offset();
@@ -457,10 +468,25 @@ void PopupMenu::_notification(int p_what) {
hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
}
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
+
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ if (text != String()) {
+ int ss = font->get_string_size(text).width;
+ int center = (get_size().width) / 2;
+ int l = center - ss / 2;
+ int r = center + ss / 2;
+ if (l > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ }
+ if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
+ labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ }
+ } else {
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ }
}
if (items[i].checkable_type) {
@@ -480,8 +506,13 @@ void PopupMenu::_notification(int p_what) {
}
item_ofs.y += font->get_ascent();
- String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
- if (!items[i].separator) {
+ if (items[i].separator) {
+
+ if (text != String()) {
+ int center = (get_size().width - font->get_string_size(text).width) / 2;
+ font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
+ }
+ } else {
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
}
@@ -1064,11 +1095,15 @@ void PopupMenu::remove_item(int p_idx) {
update();
}
-void PopupMenu::add_separator() {
+void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
sep.ID = -1;
+ if (p_text != String()) {
+ sep.text = p_text;
+ sep.xl_text = tr(p_text);
+ }
items.push_back(sep);
update();
}
@@ -1201,6 +1236,19 @@ bool PopupMenu::is_hide_on_multistate_item_selection() const {
return hide_on_multistate_item_selection;
}
+void PopupMenu::set_submenu_popup_delay(float p_time) {
+
+ if (p_time <= 0)
+ p_time = 0.01;
+
+ submenu_timer->set_wait_time(p_time);
+}
+
+float PopupMenu::get_submenu_popup_delay() const {
+
+ return submenu_timer->get_wait_time();
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1288,7 +1336,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
- ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator);
+ ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
@@ -1303,12 +1351,15 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection);
+ ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay);
+ ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay);
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index d3ee9be1c0..8ec51c7d3a 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -180,7 +180,7 @@ public:
void remove_item(int p_idx);
- void add_separator();
+ void add_separator(const String &p_text = String());
void clear();
@@ -202,6 +202,9 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ void set_submenu_popup_delay(float p_time);
+ float get_submenu_popup_delay() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
PopupMenu();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f34559fc8d..857ae8ff4c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -324,7 +324,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
color = _find_color(text, p_base_color);
font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
- if (_find_meta(text, &meta)) {
+ if (_find_meta(text, &meta) && underline_meta) {
underline = true;
}
@@ -1253,6 +1253,9 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
//validate invalid lines
Size2 size = get_size();
+ if (fixed_width != -1) {
+ size.width = fixed_width;
+ }
Rect2 text_rect = _get_text_rect();
Color font_color_shadow = get_color("font_color_shadow");
bool use_outline = get_constant("shadow_as_outline");
@@ -2245,6 +2248,21 @@ int RichTextLabel::get_total_character_count() const {
return tc;
}
+void RichTextLabel::set_fixed_size_to_width(int p_width) {
+ fixed_width = p_width;
+ minimum_size_changed();
+}
+
+Size2 RichTextLabel::get_minimum_size() const {
+
+ if (fixed_width != -1) {
+ const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
+ return Size2(fixed_width, const_cast<RichTextLabel *>(this)->get_content_height());
+ }
+
+ return Size2();
+}
+
RichTextLabel::RichTextLabel() {
main = memnew(ItemFrame);
@@ -2287,6 +2305,7 @@ RichTextLabel::RichTextLabel() {
percent_visible = 1;
visible_line_count = 0;
+ fixed_width = -1;
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index af368af46a..06e9b8efe3 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -293,6 +293,8 @@ private:
void _update_all_lines();
+ int fixed_width;
+
protected:
void _notification(int p_what);
@@ -368,6 +370,9 @@ public:
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ void set_fixed_size_to_width(int p_width);
+ virtual Size2 get_minimum_size() const;
+
RichTextLabel();
~RichTextLabel();
};
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 6ec67aca6b..e5bd1c453d 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -257,9 +257,7 @@ void ScrollBar::_notification(int p_what) {
Point2 ofs;
- VisualServer *vs = VisualServer::get_singleton();
-
- vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid());
+ decr->draw(ci, Point2());
if (orientation == HORIZONTAL)
ofs.x += decr->get_width();
@@ -280,7 +278,7 @@ void ScrollBar::_notification(int p_what) {
else
ofs.height += area.height;
- vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid());
+ incr->draw(ci, ofs);
Rect2 grabber_rect;
if (orientation == HORIZONTAL) {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 46215c9277..b820e2eafd 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -65,11 +65,12 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
} else {
grab.active = false;
}
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
-
- set_value(get_value() + get_step());
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
- set_value(get_value() - get_step());
+ } else if (scrollable) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ set_value(get_value() + get_step());
+ } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ set_value(get_value() - get_step());
+ }
}
}
@@ -247,6 +248,16 @@ bool Slider::is_editable() const {
return editable;
}
+void Slider::set_scrollable(bool p_scrollable) {
+
+ scrollable = p_scrollable;
+}
+
+bool Slider::is_scrollable() const {
+
+ return scrollable;
+}
+
void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Slider::_gui_input);
@@ -258,8 +269,11 @@ void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_editable", "editable"), &Slider::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &Slider::is_editable);
+ ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable);
+ ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
@@ -272,5 +286,6 @@ Slider::Slider(Orientation p_orientation) {
ticks = 0;
custom_step = -1;
editable = true;
+ scrollable = true;
set_focus_mode(FOCUS_ALL);
}
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index e77a0b7423..4d02348159 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -48,6 +48,7 @@ class Slider : public Range {
Orientation orientation;
float custom_step;
bool editable;
+ bool scrollable;
protected:
void _gui_input(Ref<InputEvent> p_event);
@@ -70,6 +71,9 @@ public:
void set_editable(bool p_editable);
bool is_editable() const;
+ void set_scrollable(bool p_scrollable);
+ bool is_scrollable() const;
+
Slider(Orientation p_orientation = VERTICAL);
};
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 4f72b5c6ed..c30fa96327 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -407,7 +407,7 @@ void TabContainer::_child_renamed_callback() {
void TabContainer::add_child_notify(Node *p_child) {
- Control::add_child_notify(p_child);
+ Container::add_child_notify(p_child);
Control *c = Object::cast_to<Control>(p_child);
if (!c)
@@ -515,7 +515,7 @@ Control *TabContainer::get_current_tab_control() const {
void TabContainer::remove_child_notify(Node *p_child) {
- Control::remove_child_notify(p_child);
+ Container::remove_child_notify(p_child);
call_deferred("_update_current_tab");
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 1afe5f7541..8a3c9d2bb2 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -31,11 +31,11 @@
#ifndef TAB_CONTAINER_H
#define TAB_CONTAINER_H
-#include "scene/gui/control.h"
+#include "scene/gui/container.h"
#include "scene/gui/popup.h"
-class TabContainer : public Control {
+class TabContainer : public Container {
- GDCLASS(TabContainer, Control);
+ GDCLASS(TabContainer, Container);
public:
enum TabAlign {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 215ba0271f..90cb475a7b 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -290,6 +290,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
Line line;
line.marked = false;
+ line.safe = false;
line.breakpoint = false;
line.hidden = false;
line.width_cache = -1;
@@ -426,6 +427,9 @@ void TextEdit::_update_scrollbars() {
void TextEdit::_click_selection_held() {
+ // Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
+ // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
+ // I'm unsure if there's an actual fix that doesn't have a ton of side effects.
if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) {
switch (selection.selecting_mode) {
case Selection::MODE_POINTER: {
@@ -455,7 +459,7 @@ void TextEdit::_update_selection_mode_pointer() {
select(selection.selecting_line, selection.selecting_column, row, col);
cursor_set_line(row, false);
- cursor_set_column(col, false);
+ cursor_set_column(col);
update();
click_select_held->start();
@@ -496,21 +500,24 @@ void TextEdit::_update_selection_mode_word() {
selection.selected_word_beg = beg;
selection.selected_word_end = end;
selection.selected_word_origin = beg;
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
} else {
if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) {
selection.selecting_column = selection.selected_word_end;
select(row, beg, selection.selecting_line, selection.selected_word_end);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
} else {
selection.selecting_column = selection.selected_word_beg;
select(selection.selecting_line, selection.selected_word_beg, row, end);
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
}
}
- cursor_set_line(row, false);
update();
+
click_select_held->start();
}
@@ -531,7 +538,7 @@ void TextEdit::_update_selection_mode_line() {
selection.selecting_column = 0;
col = text[row].length();
}
- cursor_set_column(0, false);
+ cursor_set_column(0);
select(selection.selecting_line, selection.selecting_column, row, col);
update();
@@ -960,12 +967,13 @@ void TextEdit::_notification(int p_what) {
// draw line numbers
if (cache.line_number_w) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
String fc = String::num(line + 1);
while (fc.length() < line_number_char_count) {
fc = line_num_padding + fc;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
+ cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
}
}
@@ -1088,12 +1096,13 @@ void TextEdit::_notification(int p_what) {
}
if (brace_matching_enabled) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) ||
(cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
@@ -1101,7 +1110,7 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
@@ -1161,9 +1170,10 @@ void TextEdit::_notification(int p_what) {
}
if (str[j] >= 32) {
- int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
+ int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
} else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
@@ -1528,8 +1538,11 @@ void TextEdit::backspace_at_cursor() {
if (is_line_hidden(cursor.line))
set_line_as_hidden(prev_line, true);
- if (is_line_set_as_breakpoint(cursor.line))
+ if (is_line_set_as_breakpoint(cursor.line)) {
+ if (!text.is_breakpoint(prev_line))
+ emit_signal("breakpoint_toggled", prev_line);
set_line_as_breakpoint(prev_line, true);
+ }
if (auto_brace_completion_enabled &&
cursor.column > 0 &&
@@ -1660,14 +1673,17 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
rows /= get_row_height();
rows += get_v_scroll_offset();
int first_vis_line = get_first_visible_line();
+ int last_vis_line = get_last_visible_line();
int row = first_vis_line + Math::floor(rows);
int wrap_index = 0;
if (is_wrap_enabled() || is_hiding_enabled()) {
- int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + 1, wrap_index) - 1;
- row = first_vis_line + f_ofs;
- row = CLAMP(row, 0, get_last_visible_line() + 1);
+ int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
+ if (rows < 0)
+ row = first_vis_line - f_ofs;
+ else
+ row = first_vis_line + f_ofs;
}
if (row < 0)
@@ -3301,22 +3317,37 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
ERR_FAIL_INDEX(p_line, text.size());
ERR_FAIL_COND(p_char < 0);
- /* STEP 1 add spaces if the char is greater than the end of the line */
+ /* STEP 1 remove \r from source text and separate in substrings */
+
+ Vector<String> substrings = p_text.replace("\r", "").split("\n");
+
+ /* STEP 2 fire breakpoint_toggled signals */
+
+ // Is this just a new empty line?
+ bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+
+ int i = p_line + !shift_first_line;
+ int lines = substrings.size() - 1;
+ for (; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if ((i - lines < p_line || !text.is_breakpoint(i - lines)) || (i - lines == p_line && !shift_first_line))
+ emit_signal("breakpoint_toggled", i);
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i + lines);
+ }
+ }
+
+ /* STEP 3 add spaces if the char is greater than the end of the line */
while (p_char > text[p_line].length()) {
text.set(p_line, text[p_line] + String::chr(' '));
}
- /* STEP 2 separate dest string in pre and post text */
+ /* STEP 4 separate dest string in pre and post text */
String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
- /* STEP 3 remove \r from source text and separate in substrings */
-
- //buh bye \r and split
- Vector<String> substrings = p_text.replace("\r", "").split("\n");
-
for (int i = 0; i < substrings.size(); i++) {
//insert the substrings
@@ -3334,9 +3365,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
}
- // if we are just making a new empty line, reset breakpoints and hidden status
- if (p_char == 0 && p_text.replace("\r", "") == "\n") {
-
+ if (shift_first_line) {
text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
text.set_hidden(p_line + 1, text.is_hidden(p_line));
text.set_breakpoint(p_line, false);
@@ -3392,11 +3421,20 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- for (int i = p_from_line; i < p_to_line; i++) {
+ int lines = p_to_line - p_from_line;
- text.remove(p_from_line + 1);
+ for (int i = p_from_line + 1; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i);
+ if (i > p_to_line && (i - lines < 0 || !text.is_breakpoint(i - lines)))
+ emit_signal("breakpoint_toggled", i - lines);
+ }
}
+ for (int i = p_from_line; i < p_to_line; i++) {
+ text.remove(p_from_line + 1);
+ }
text.set(p_from_line, pre_text + post_text);
text.set_line_wrap_amount(p_from_line, -1);
@@ -4277,6 +4315,7 @@ void TextEdit::_update_caches() {
cache.caret_color = get_color("caret_color");
cache.caret_background_color = get_color("caret_background_color");
cache.line_number_color = get_color("line_number_color");
+ cache.safe_line_number_color = get_color("safe_line_number_color");
cache.font_color = get_color("font_color");
cache.font_selected_color = get_color("font_selected_color");
cache.keyword_color = get_color("keyword_color");
@@ -4848,6 +4887,17 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
update();
}
+void TextEdit::set_line_as_safe(int p_line, bool p_safe) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_safe(p_line, p_safe);
+ update();
+}
+
+bool TextEdit::is_line_set_as_safe(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ return text.is_safe(p_line);
+}
+
bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
@@ -4869,6 +4919,24 @@ void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
}
}
+Array TextEdit::get_breakpoints_array() const {
+
+ Array arr;
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ arr.append(i);
+ }
+ return arr;
+}
+
+void TextEdit::remove_breakpoints() {
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ /* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
+ text.set_breakpoint(i, false);
+ }
+}
+
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5538,7 +5606,17 @@ void TextEdit::_confirm_completion() {
cursor_set_column(cursor.column - completion_base.length(), false);
insert_text_at_cursor(completion_current);
- if (completion_current.ends_with("(") && auto_brace_completion_enabled) {
+ // When inserted into the middle of an existing string, don't add an unnecessary quote
+ String line = text[cursor.line];
+ CharType next_char = line[cursor.column];
+ CharType last_completion_char = completion_current[completion_current.length() - 1];
+
+ if ((last_completion_char == '"' || last_completion_char == '\'') &&
+ last_completion_char == next_char) {
+ _base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ }
+
+ if (last_completion_char == '(' && auto_brace_completion_enabled) {
insert_text_at_cursor(")");
cursor.column--;
}
@@ -5765,18 +5843,23 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
+ char selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
- if (s[i] == '"') {
- if (inside_quotes) {
- qend = i;
- inside_quotes = false;
- if (col >= qbegin && col <= qend) {
- return s.substr(qbegin, qend - qbegin);
+ if (s[i] == '"' || s[i] == '\'') {
+ if (i == 0 || s[i - 1] != '\\') {
+ if (inside_quotes && selected_quote == s[i]) {
+ qend = i;
+ inside_quotes = false;
+ selected_quote = '\0';
+ if (col >= qbegin && col <= qend) {
+ return s.substr(qbegin, qend - qbegin);
+ }
+ } else if (!inside_quotes) {
+ qbegin = i + 1;
+ inside_quotes = true;
+ selected_quote = s[i];
}
- } else {
- qbegin = i + 1;
- inside_quotes = true;
}
}
}
@@ -5857,12 +5940,12 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
-void TextEdit::set_draw_breakpoint_gutter(bool p_draw) {
+void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
}
-bool TextEdit::is_drawing_breakpoint_gutter() const {
+bool TextEdit::is_breakpoint_gutter_enabled() const {
return draw_breakpoint_gutter;
}
@@ -6045,6 +6128,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
+ ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6083,11 +6168,15 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
+ ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
+ ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 5c82d1ac20..34d69bb508 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -76,6 +76,7 @@ public:
bool marked : 1;
bool breakpoint : 1;
bool hidden : 1;
+ bool safe : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
String data;
@@ -106,6 +107,8 @@ public:
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
+ void set_safe(int p_line, bool p_safe) { text[p_line].safe = p_safe; }
+ bool is_safe(int p_line) const { return text[p_line].safe; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@@ -165,6 +168,7 @@ private:
Color caret_color;
Color caret_background_color;
Color line_number_color;
+ Color safe_line_number_color;
Color font_color;
Color font_selected_color;
Color keyword_color;
@@ -472,7 +476,11 @@ public:
void set_line_as_marked(int p_line, bool p_marked);
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
+ void set_line_as_safe(int p_line, bool p_safe);
+ bool is_line_set_as_safe(int p_line) const;
void get_breakpoints(List<int> *p_breakpoints) const;
+ Array get_breakpoints_array() const;
+ void remove_breakpoints();
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@@ -632,8 +640,8 @@ public:
void set_show_line_length_guideline(bool p_show);
void set_line_length_guideline_column(int p_column);
- void set_draw_breakpoint_gutter(bool p_draw);
- bool is_drawing_breakpoint_gutter() const;
+ void set_breakpoint_gutter_enabled(bool p_draw);
+ bool is_breakpoint_gutter_enabled() const;
void set_breakpoint_gutter_width(int p_gutter_width);
int get_breakpoint_gutter_width() const;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index ae21775c55..4750e05633 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -30,8 +30,6 @@
#include "http_request.h"
-#include "version.h"
-
void HTTPRequest::_redirect_request(const String &p_new_url) {
}
@@ -106,28 +104,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
validate_ssl = p_ssl_validate_domain;
- bool has_user_agent = false;
- bool has_accept = false;
headers = p_custom_headers;
request_data = p_request_data;
- for (int i = 0; i < headers.size(); i++) {
-
- if (headers[i].findn("user-agent:") == 0)
- has_user_agent = true;
- if (headers[i].findn("Accept:") == 0)
- has_accept = true;
- }
-
- if (!has_user_agent) {
- headers.push_back("User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")");
- }
-
- if (!has_accept) {
- headers.push_back("Accept: */*");
- }
-
requesting = true;
if (use_threads) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 4dc7b03685..b7b26d1c55 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() {
void Node::_propagate_exit_tree() {
-//block while removing children
+ //block while removing children
#ifdef DEBUG_ENABLED
@@ -809,6 +809,22 @@ bool Node::is_processing_internal() const {
return data.idle_process_internal;
}
+void Node::set_process_priority(int p_priority) {
+ data.process_priority = p_priority;
+
+ if (is_processing())
+ data.tree->make_group_changed("idle_process");
+
+ if (is_processing_internal())
+ data.tree->make_group_changed("idle_process_internal");
+
+ if (is_physics_processing())
+ data.tree->make_group_changed("physics_process");
+
+ if (is_physics_processing_internal())
+ data.tree->make_group_changed("physics_process_internal");
+}
+
void Node::set_process_input(bool p_enable) {
if (p_enable == data.input)
@@ -1388,6 +1404,11 @@ bool Node::is_greater_than(const Node *p_node) const {
return res;
}
+bool Node::has_priority_higher_than(const Node *p_node) const {
+ ERR_FAIL_NULL_V(p_node, false);
+ return data.process_priority > p_node->data.process_priority;
+}
+
void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) {
if (data.owner == p_by)
@@ -1895,7 +1916,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
// Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later
// but remember non-instanced nodes that are hidden below instanced ones
if (descendant->data.owner != this) {
- if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this)
+ if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this && descendant->data.owner != descendant->get_parent())
hidden_roots.push_back(descendant);
continue;
}
@@ -1934,8 +1955,9 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) {
Resource *res = Object::cast_to<Resource>(value);
- if (res) // Duplicate only if it's a resource
+ if (res) { // Duplicate only if it's a resource
current_node->set(name, res->duplicate());
+ }
} else {
@@ -2607,6 +2629,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_physics_processing"), &Node::is_physics_processing);
ClassDB::bind_method(D_METHOD("get_process_delta_time"), &Node::get_process_delta_time);
ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process);
+ ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority);
ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing);
ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input);
ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input);
@@ -2758,6 +2781,7 @@ Node::Node() {
data.tree = NULL;
data.physics_process = false;
data.idle_process = false;
+ data.process_priority = 0;
data.physics_process_internal = false;
data.idle_process_internal = false;
data.inside_tree = false;
diff --git a/scene/main/node.h b/scene/main/node.h
index 341349de79..4b8f584ba7 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -70,6 +70,11 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
+ struct ComparatorWithPriority {
+
+ bool operator()(const Node *p_a, const Node *p_b) const { return p_b->has_priority_higher_than(p_a) || p_b->is_greater_than(p_a); }
+ };
+
private:
struct GroupData {
@@ -118,6 +123,7 @@ private:
//should move all the stuff below to bits
bool physics_process;
bool idle_process;
+ int process_priority;
bool physics_process_internal;
bool idle_process_internal;
@@ -259,6 +265,7 @@ public:
bool is_a_parent_of(const Node *p_node) const;
bool is_greater_than(const Node *p_node) const;
+ bool has_priority_higher_than(const Node *p_node) const;
NodePath get_path() const;
NodePath get_path_to(const Node *p_node) const;
@@ -319,6 +326,8 @@ public:
void set_process_internal(bool p_idle_process_internal);
bool is_processing_internal() const;
+ void set_process_priority(int p_priority);
+
void set_process_input(bool p_enable);
bool is_processing_input() const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 8d6e57b335..3424c4edac 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -132,6 +132,12 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
group_map.erase(E);
}
+void SceneTree::make_group_changed(const StringName &p_group) {
+ Map<StringName, Group>::Element *E = group_map.find(p_group);
+ if (E)
+ E->get().changed = true;
+}
+
void SceneTree::flush_transform_notifications() {
SelfList<Node> *n = xform_change_list.first();
@@ -165,7 +171,7 @@ void SceneTree::_flush_ugc() {
ugc_locked = false;
}
-void SceneTree::_update_group_order(Group &g) {
+void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
if (!g.changed)
return;
@@ -175,8 +181,13 @@ void SceneTree::_update_group_order(Group &g) {
Node **nodes = &g.nodes[0];
int node_count = g.nodes.size();
- SortArray<Node *, Node::Comparator> node_sort;
- node_sort.sort(nodes, node_count);
+ if (p_use_priority) {
+ SortArray<Node *, Node::ComparatorWithPriority> node_sort;
+ node_sort.sort(nodes, node_count);
+ } else {
+ SortArray<Node *, Node::Comparator> node_sort;
+ node_sort.sort(nodes, node_count);
+ }
g.changed = false;
}
@@ -498,7 +509,8 @@ bool SceneTree::idle(float p_time) {
_notify_group_pause("idle_process_internal", Node::NOTIFICATION_INTERNAL_PROCESS);
_notify_group_pause("idle_process", Node::NOTIFICATION_PROCESS);
- Size2 win_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 win_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
+
if (win_size != last_screen_size) {
last_screen_size = win_size;
@@ -656,6 +668,11 @@ void SceneTree::_notification(int p_notification) {
#endif
} break;
+ case NOTIFICATION_CRASH: {
+
+ get_root()->propagate_notification(p_notification);
+ } break;
+
default:
break;
};
@@ -915,7 +932,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
if (g.nodes.empty())
return;
- _update_group_order(g);
+ _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
//copy, so copy on write happens in case something is removed from process while being called
//performance is not lost because only if something is added/removed the vector is copied.
@@ -1112,7 +1129,7 @@ void SceneTree::_update_root_rect() {
}
//actual screen video mode
- Size2 video_mode = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 video_mode = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
Size2 desired_res = stretch_min;
Size2 viewport_size;
@@ -2004,7 +2021,7 @@ SceneTree::SceneTree() {
stretch_aspect = STRETCH_ASPECT_IGNORE;
stretch_shrink = 1;
- last_screen_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index aa8d78b1e1..11201097d4 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -161,7 +161,7 @@ private:
bool ugc_locked;
void _flush_ugc();
- _FORCE_INLINE_ void _update_group_order(Group &g);
+ _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false);
void _update_listener();
Array _get_nodes_in_group(const StringName &p_group);
@@ -204,6 +204,7 @@ private:
Group *add_to_group(const StringName &p_group, Node *p_node);
void remove_from_group(const StringName &p_group, Node *p_node);
+ void make_group_changed(const StringName &p_group);
void _notify_group_pause(const StringName &p_group, int p_notification);
void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1236aea2dd..9013d276c7 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -41,7 +41,7 @@
#include "scene/3d/spatial.h"
#include "scene/gui/control.h"
#include "scene/gui/label.h"
-#include "scene/gui/panel.h"
+#include "scene/gui/panel_container.h"
#include "scene/main/timer.h"
#include "scene/resources/mesh.h"
#include "scene/scene_string_names.h"
@@ -144,7 +144,7 @@ void ViewportTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path"), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport"), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
}
ViewportTexture::ViewportTexture() {
@@ -166,9 +166,9 @@ ViewportTexture::~ViewportTexture() {
/////////////////////////////////////
-class TooltipPanel : public Panel {
+class TooltipPanel : public PanelContainer {
- GDCLASS(TooltipPanel, Panel)
+ GDCLASS(TooltipPanel, PanelContainer)
public:
TooltipPanel(){};
};
@@ -626,7 +626,7 @@ Rect2 Viewport::get_visible_rect() const {
if (size == Size2()) {
- r = Rect2(Point2(), Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height));
+ r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height));
} else {
r = Rect2(Point2(), size);
@@ -1305,10 +1305,11 @@ void Viewport::_gui_cancel_tooltip() {
if (gui.tooltip_popup) {
gui.tooltip_popup->queue_delete();
gui.tooltip_popup = NULL;
+ gui.tooltip_label = NULL;
}
}
-String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) {
+String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which) {
Vector2 pos = p_pos;
String tooltip;
@@ -1317,6 +1318,10 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) {
tooltip = p_control->get_tooltip(pos);
+ if (r_which) {
+ *r_which = p_control;
+ }
+
if (tooltip != String())
break;
pos = p_control->get_transform().xform(pos);
@@ -1338,41 +1343,49 @@ void Viewport::_gui_show_tooltip() {
return;
}
- String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos));
+ Control *which = NULL;
+ String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos), &which);
if (tooltip.length() == 0)
return; // bye
if (gui.tooltip_popup) {
memdelete(gui.tooltip_popup);
gui.tooltip_popup = NULL;
+ gui.tooltip_label = NULL;
}
- if (!gui.tooltip) {
+ if (!which) {
return;
}
- Control *rp = gui.tooltip->get_root_parent_control();
+ Control *rp = which; //->get_root_parent_control();
if (!rp)
return;
- gui.tooltip_popup = memnew(TooltipPanel);
+ gui.tooltip_popup = which->make_custom_tooltip(tooltip);
+
+ if (!gui.tooltip_popup) {
+ gui.tooltip_popup = memnew(TooltipPanel);
+
+ gui.tooltip_label = memnew(TooltipLabel);
+ gui.tooltip_popup->add_child(gui.tooltip_label);
+
+ Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel");
+
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
+ gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
+ gui.tooltip_label->set_text(tooltip.strip_edges());
+ }
rp->add_child(gui.tooltip_popup);
gui.tooltip_popup->force_parent_owned();
- gui.tooltip_label = memnew(TooltipLabel);
- gui.tooltip_popup->add_child(gui.tooltip_label);
gui.tooltip_popup->set_as_toplevel(true);
- gui.tooltip_popup->hide();
-
- Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel");
+ //gui.tooltip_popup->hide();
- gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
- gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
- gui.tooltip_label->set_text(tooltip.strip_edges());
- Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size());
- Rect2 vr = gui.tooltip_label->get_viewport_rect();
+ Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_popup->get_minimum_size());
+ Rect2 vr = gui.tooltip_popup->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
else if (r.position.x < 0)
@@ -1404,6 +1417,8 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
mb->get_button_index() == BUTTON_WHEEL_UP ||
mb->get_button_index() == BUTTON_WHEEL_LEFT ||
mb->get_button_index() == BUTTON_WHEEL_RIGHT));
+ Ref<InputEventPanGesture> pn = p_input;
+ cant_stop_me_now = pn.is_valid() || cant_stop_me_now;
bool ismouse = ev.is_valid() || Object::cast_to<InputEventMouseMotion>(*p_input) != NULL;
@@ -1889,13 +1904,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
bool is_tooltip_shown = false;
if (gui.tooltip_popup) {
- if (can_tooltip) {
+ if (can_tooltip && gui.tooltip) {
String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos));
if (tooltip.length() == 0)
_gui_cancel_tooltip();
- else if (tooltip == gui.tooltip_label->get_text())
+ else if (gui.tooltip_label) {
+ if (tooltip == gui.tooltip_label->get_text()) {
+ is_tooltip_shown = true;
+ }
+ } else if (tooltip == String(gui.tooltip_popup->call("get_tooltip_text"))) {
is_tooltip_shown = true;
+ }
} else
_gui_cancel_tooltip();
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 3000398540..e717d27069 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -255,7 +255,7 @@ private:
Control *key_focus;
Control *mouse_over;
Control *tooltip;
- Panel *tooltip_popup;
+ Control *tooltip_popup;
Label *tooltip_label;
Point2 tooltip_pos;
Point2 last_mouse_pos;
@@ -312,7 +312,7 @@ private:
void _gui_remove_root_control(List<Control *>::Element *RI);
void _gui_remove_subwindow_control(List<Control *>::Element *SI);
- String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos);
+ String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which = NULL);
void _gui_cancel_tooltip();
void _gui_show_tooltip();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 244d95d9c7..0d69c037fe 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -51,6 +51,7 @@
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/2d/particles_2d.h"
+
#include "scene/2d/path_2d.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/2d/polygon_2d.h"
@@ -63,11 +64,14 @@
#include "scene/2d/tile_map.h"
#include "scene/2d/visibility_notifier_2d.h"
#include "scene/2d/y_sort.h"
-#include "scene/animation/animation_blend_space.h"
+#include "scene/animation/animation_blend_space_1d.h"
+#include "scene/animation/animation_blend_space_2d.h"
#include "scene/animation/animation_blend_tree.h"
-#include "scene/animation/animation_graph_player.h"
+#include "scene/animation/animation_node_state_machine.h"
#include "scene/animation/animation_player.h"
+#include "scene/animation/animation_tree.h"
#include "scene/animation/animation_tree_player.h"
+#include "scene/animation/root_motion_view.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_player.h"
#include "scene/gui/box_container.h"
@@ -131,6 +135,7 @@
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/cylinder_shape.h"
#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/dynamic_font_stb.h"
@@ -154,10 +159,13 @@
#include "scene/resources/texture.h"
#include "scene/resources/tile_set.h"
#include "scene/resources/video_stream.h"
+#include "scene/resources/visual_shader.h"
+#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/world.h"
#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
+#include "scene/3d/cpu_particles.h"
#include "scene/3d/particles.h"
#include "scene/3d/scenario_fx.h"
#include "scene/3d/spatial.h"
@@ -191,10 +199,12 @@
#include "scene/3d/remote_transform.h"
#include "scene/3d/room_instance.h"
#include "scene/3d/skeleton.h"
+#include "scene/3d/soft_body.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
#include "scene/resources/environment.h"
+#include "scene/resources/physics_material.h"
#endif
static ResourceFormatLoaderTheme *resource_loader_theme = NULL;
@@ -304,21 +314,19 @@ void register_scene_types() {
ClassDB::register_class<CenterContainer>();
ClassDB::register_class<ScrollContainer>();
ClassDB::register_class<PanelContainer>();
- ClassDB::register_virtual_class<SplitContainer>();
- ClassDB::register_class<HSplitContainer>();
- ClassDB::register_class<VSplitContainer>();
- ClassDB::register_class<GraphNode>();
- ClassDB::register_class<GraphEdit>();
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<TextureProgress>();
ClassDB::register_class<ItemList>();
+ ClassDB::register_class<LineEdit>();
+ ClassDB::register_class<VideoPlayer>();
+
#ifndef ADVANCED_GUI_DISABLED
ClassDB::register_class<FileDialog>();
- ClassDB::register_class<LineEdit>();
+
ClassDB::register_class<PopupMenu>();
ClassDB::register_class<Tree>();
@@ -335,9 +343,13 @@ void register_scene_types() {
ClassDB::register_class<WindowDialog>();
ClassDB::register_class<AcceptDialog>();
ClassDB::register_class<ConfirmationDialog>();
- ClassDB::register_class<VideoPlayer>();
ClassDB::register_class<MarginContainer>();
ClassDB::register_class<ViewportContainer>();
+ ClassDB::register_virtual_class<SplitContainer>();
+ ClassDB::register_class<HSplitContainer>();
+ ClassDB::register_class<VSplitContainer>();
+ ClassDB::register_class<GraphNode>();
+ ClassDB::register_class<GraphEdit>();
OS::get_singleton()->yield(); //may take time to init
@@ -380,20 +392,28 @@ void register_scene_types() {
ClassDB::register_class<BakedLightmapData>();
ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<Particles>();
+ ClassDB::register_class<CPUParticles>();
ClassDB::register_class<Position3D>();
ClassDB::register_class<NavigationMeshInstance>();
ClassDB::register_class<NavigationMesh>();
ClassDB::register_class<Navigation>();
- ClassDB::register_class<AnimationGraphPlayer>();
+ ClassDB::register_class<RootMotionView>();
+ ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor
+
+ ClassDB::register_class<AnimationTree>();
ClassDB::register_class<AnimationNode>();
ClassDB::register_class<AnimationRootNode>();
ClassDB::register_class<AnimationNodeBlendTree>();
- ClassDB::register_class<AnimationNodeBlendSpace>();
+ ClassDB::register_class<AnimationNodeBlendSpace1D>();
+ ClassDB::register_class<AnimationNodeBlendSpace2D>();
+ ClassDB::register_class<AnimationNodeStateMachine>();
+ ClassDB::register_class<AnimationNodeStateMachineTransition>();
ClassDB::register_class<AnimationNodeOutput>();
ClassDB::register_class<AnimationNodeOneShot>();
ClassDB::register_class<AnimationNodeAnimation>();
- ClassDB::register_class<AnimationNodeAdd>();
+ ClassDB::register_class<AnimationNodeAdd2>();
+ ClassDB::register_class<AnimationNodeAdd3>();
ClassDB::register_class<AnimationNodeBlend2>();
ClassDB::register_class<AnimationNodeBlend3>();
ClassDB::register_class<AnimationNodeTimeScale>();
@@ -409,6 +429,7 @@ void register_scene_types() {
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
ClassDB::register_class<PhysicalBone>();
+ ClassDB::register_class<SoftBody>();
ClassDB::register_class<VehicleBody>();
ClassDB::register_class<VehicleWheel>();
@@ -442,6 +463,39 @@ void register_scene_types() {
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
ClassDB::register_class<Shader>();
+ ClassDB::register_class<VisualShader>();
+ ClassDB::register_virtual_class<VisualShaderNode>();
+ ClassDB::register_class<VisualShaderNodeInput>();
+ ClassDB::register_virtual_class<VisualShaderNodeOutput>();
+ ClassDB::register_class<VisualShaderNodeScalarConstant>();
+ ClassDB::register_class<VisualShaderNodeColorConstant>();
+ ClassDB::register_class<VisualShaderNodeVec3Constant>();
+ ClassDB::register_class<VisualShaderNodeTransformConstant>();
+ ClassDB::register_class<VisualShaderNodeScalarOp>();
+ ClassDB::register_class<VisualShaderNodeVectorOp>();
+ ClassDB::register_class<VisualShaderNodeColorOp>();
+ ClassDB::register_class<VisualShaderNodeTransformMult>();
+ ClassDB::register_class<VisualShaderNodeTransformVecMult>();
+ ClassDB::register_class<VisualShaderNodeScalarFunc>();
+ ClassDB::register_class<VisualShaderNodeVectorFunc>();
+ ClassDB::register_class<VisualShaderNodeDotProduct>();
+ ClassDB::register_class<VisualShaderNodeVectorLen>();
+ ClassDB::register_class<VisualShaderNodeScalarInterp>();
+ ClassDB::register_class<VisualShaderNodeVectorInterp>();
+ ClassDB::register_class<VisualShaderNodeVectorCompose>();
+ ClassDB::register_class<VisualShaderNodeTransformCompose>();
+ ClassDB::register_class<VisualShaderNodeVectorDecompose>();
+ ClassDB::register_class<VisualShaderNodeTransformDecompose>();
+ ClassDB::register_class<VisualShaderNodeTexture>();
+ ClassDB::register_class<VisualShaderNodeCubeMap>();
+ ClassDB::register_virtual_class<VisualShaderNodeUniform>();
+ ClassDB::register_class<VisualShaderNodeScalarUniform>();
+ ClassDB::register_class<VisualShaderNodeColorUniform>();
+ ClassDB::register_class<VisualShaderNodeVec3Uniform>();
+ ClassDB::register_class<VisualShaderNodeTransformUniform>();
+ ClassDB::register_class<VisualShaderNodeTextureUniform>();
+ ClassDB::register_class<VisualShaderNodeCubeMapUniform>();
+
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
ClassDB::register_class<CanvasItemMaterial>();
@@ -528,6 +582,7 @@ void register_scene_types() {
ClassDB::register_class<SphereShape>();
ClassDB::register_class<BoxShape>();
ClassDB::register_class<CapsuleShape>();
+ ClassDB::register_class<CylinderShape>();
ClassDB::register_class<PlaneShape>();
ClassDB::register_class<ConvexPolygonShape>();
ClassDB::register_class<ConcavePolygonShape>();
@@ -538,6 +593,8 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<SpatialVelocityTracker>();
+
+ ClassDB::register_class<PhysicsMaterial>();
#endif
ClassDB::register_class<World>();
ClassDB::register_class<Environment>();
@@ -553,6 +610,7 @@ void register_scene_types() {
ClassDB::register_class<CurveTexture>();
ClassDB::register_class<GradientTexture>();
ClassDB::register_class<ProxyTexture>();
+ ClassDB::register_class<AnimatedTexture>();
ClassDB::register_class<CubeMap>();
ClassDB::register_class<Animation>();
ClassDB::register_virtual_class<Font>();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index fe4d687c23..8acfdc482a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -134,8 +134,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
int um = d["update"];
if (um < 0)
um = 0;
- else if (um > 2)
- um = 2;
+ else if (um > 3)
+ um = 3;
vt->update_mode = UpdateMode(um);
}
@@ -2329,13 +2329,14 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
int iterations = 10;
- float low = 0;
- float high = bt->values[idx + 1].time - bt->values[idx].time;
+ float duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
+ float low = 0; // 0% of the current animation segment
+ float high = 1; // 100% of the current animation segment
float middle = 0;
Vector2 start(0, bt->values[idx].value.value);
Vector2 start_out = start + bt->values[idx].value.out_handle;
- Vector2 end(high, bt->values[idx + 1].value.value);
+ Vector2 end(duration, bt->values[idx + 1].value.value);
Vector2 end_in = end + bt->values[idx + 1].value.in_handle;
//narrow high and low as much as possible
@@ -2355,7 +2356,6 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
-
float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.linear_interpolate(high_pos, c).y;
@@ -2363,7 +2363,6 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
int Animation::audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset, float p_end_offset) {
- print_line("really insert key? ");
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 7f902fc982..f2fd919f20 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -479,6 +479,16 @@ real_t Curve::interpolate_baked(real_t offset) {
}
}
+void Curve::ensure_default_setup(float p_min, float p_max) {
+ if (_points.size() == 0 && _min_value == 0 && _max_value == 1) {
+
+ add_point(Vector2(0, 1));
+ add_point(Vector2(1, 1));
+ set_min_value(p_min);
+ set_max_value(p_max);
+ }
+}
+
void Curve::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE));
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 9cb12a4345..058c4f1bc2 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -128,6 +128,8 @@ public:
void set_bake_resolution(int p_resolution);
real_t interpolate_baked(real_t offset);
+ void ensure_default_setup(float p_min, float p_max);
+
protected:
static void _bind_methods();
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
new file mode 100644
index 0000000000..f760462d49
--- /dev/null
+++ b/scene/resources/cylinder_shape.cpp
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* cylinder_shape.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cylinder_shape.h"
+#include "servers/physics_server.h"
+
+Vector<Vector3> CylinderShape::_gen_debug_mesh_lines() {
+
+ float radius = get_radius();
+ float height = get_height();
+
+ Vector<Vector3> points;
+
+ Vector3 d(0, height * 0.5, 0);
+ for (int i = 0; i < 360; i++) {
+
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(b.x, 0, b.y) + d);
+
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ points.push_back(Vector3(b.x, 0, b.y) - d);
+
+ if (i % 90 == 0) {
+
+ points.push_back(Vector3(a.x, 0, a.y) + d);
+ points.push_back(Vector3(a.x, 0, a.y) - d);
+ }
+ }
+
+ return points;
+}
+
+void CylinderShape::_update_shape() {
+
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+}
+
+void CylinderShape::set_radius(float p_radius) {
+
+ radius = p_radius;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("radius");
+}
+
+float CylinderShape::get_radius() const {
+
+ return radius;
+}
+
+void CylinderShape::set_height(float p_height) {
+
+ height = p_height;
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("height");
+}
+
+float CylinderShape::get_height() const {
+
+ return height;
+}
+
+void CylinderShape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CylinderShape::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CylinderShape::get_radius);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape::get_height);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+}
+
+CylinderShape::CylinderShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CYLINDER)) {
+
+ radius = 1.0;
+ height = 2.0;
+ _update_shape();
+}
diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h
new file mode 100644
index 0000000000..f510758e91
--- /dev/null
+++ b/scene/resources/cylinder_shape.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* cylinder_shape.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CYLINDER_SHAPE_H
+#define CYLINDER_SHAPE_H
+
+#include "scene/resources/shape.h"
+
+class CylinderShape : public Shape {
+
+ GDCLASS(CylinderShape, Shape);
+ float radius;
+ float height;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape();
+
+ virtual Vector<Vector3> _gen_debug_mesh_lines();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+ void set_height(float p_height);
+ float get_height() const;
+
+ CylinderShape();
+};
+
+#endif // CYLINDER_SHAPE_H
diff --git a/scene/resources/default_theme/arrow_down.png b/scene/resources/default_theme/arrow_down.png
index fc837d120a..bfb87a4761 100644
--- a/scene/resources/default_theme/arrow_down.png
+++ b/scene/resources/default_theme/arrow_down.png
Binary files differ
diff --git a/scene/resources/default_theme/arrow_right.png b/scene/resources/default_theme/arrow_right.png
index ebe6e26ace..1e4c8e5529 100644
--- a/scene/resources/default_theme/arrow_right.png
+++ b/scene/resources/default_theme/arrow_right.png
Binary files differ
diff --git a/scene/resources/default_theme/background.png b/scene/resources/default_theme/background.png
index 03cfab1de3..6c5f43e3ce 100644
--- a/scene/resources/default_theme/background.png
+++ b/scene/resources/default_theme/background.png
Binary files differ
diff --git a/scene/resources/default_theme/base_green.png b/scene/resources/default_theme/base_green.png
index d03d6f08d8..03a5b313d7 100644
--- a/scene/resources/default_theme/base_green.png
+++ b/scene/resources/default_theme/base_green.png
Binary files differ
diff --git a/scene/resources/default_theme/button_disabled.png b/scene/resources/default_theme/button_disabled.png
index d75e76989d..708748dfe9 100644
--- a/scene/resources/default_theme/button_disabled.png
+++ b/scene/resources/default_theme/button_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/button_focus.png b/scene/resources/default_theme/button_focus.png
index 44e354be95..70e16b953b 100644
--- a/scene/resources/default_theme/button_focus.png
+++ b/scene/resources/default_theme/button_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/button_hover.png b/scene/resources/default_theme/button_hover.png
index 6e609f435f..ef226e3caf 100644
--- a/scene/resources/default_theme/button_hover.png
+++ b/scene/resources/default_theme/button_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/button_normal.png b/scene/resources/default_theme/button_normal.png
index 92482aaf28..7d0bd16221 100644
--- a/scene/resources/default_theme/button_normal.png
+++ b/scene/resources/default_theme/button_normal.png
Binary files differ
diff --git a/scene/resources/default_theme/checked.png b/scene/resources/default_theme/checked.png
index 93e291a29e..bde031b6a2 100644
--- a/scene/resources/default_theme/checked.png
+++ b/scene/resources/default_theme/checked.png
Binary files differ
diff --git a/scene/resources/default_theme/checker_bg.png b/scene/resources/default_theme/checker_bg.png
index f58dfed29c..3eff2f0e08 100644
--- a/scene/resources/default_theme/checker_bg.png
+++ b/scene/resources/default_theme/checker_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/close.png b/scene/resources/default_theme/close.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/close.png
+++ b/scene/resources/default_theme/close.png
Binary files differ
diff --git a/scene/resources/default_theme/close_hl.png b/scene/resources/default_theme/close_hl.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/close_hl.png
+++ b/scene/resources/default_theme/close_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/color_picker_sample.png b/scene/resources/default_theme/color_picker_sample.png
index b145a3e384..e6ec28d307 100644
--- a/scene/resources/default_theme/color_picker_sample.png
+++ b/scene/resources/default_theme/color_picker_sample.png
Binary files differ
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index d64e6970bf..fe12e2f5f6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -476,6 +476,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("symbol_color", "TextEdit", control_font_color_hover);
theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
theme->set_color("line_number_color", "TextEdit", Color::html("66aaaaaa"));
+ theme->set_color("safe_line_number_color", "TextEdit", Color::html("99aac8aa"));
theme->set_color("function_color", "TextEdit", Color::html("66a2ce"));
theme->set_color("member_variable_color", "TextEdit", Color::html("e64e59"));
theme->set_color("number_color", "TextEdit", Color::html("EB9532"));
@@ -585,6 +586,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4));
theme->set_stylebox("hover", "PopupMenu", selected);
theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3));
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
@@ -844,7 +847,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("separation", "HBoxContainer", 4 * scale);
theme->set_constant("separation", "VBoxContainer", 4 * scale);
- theme->set_constant("margin_left", "MarginContainer", 8 * scale);
+ theme->set_constant("margin_left", "MarginContainer", 0 * scale);
theme->set_constant("margin_top", "MarginContainer", 0 * scale);
theme->set_constant("margin_right", "MarginContainer", 0 * scale);
theme->set_constant("margin_bottom", "MarginContainer", 0 * scale);
diff --git a/scene/resources/default_theme/dosfont.png b/scene/resources/default_theme/dosfont.png
index 814d2e9060..e2739b94ea 100644
--- a/scene/resources/default_theme/dosfont.png
+++ b/scene/resources/default_theme/dosfont.png
Binary files differ
diff --git a/scene/resources/default_theme/dropdown.png b/scene/resources/default_theme/dropdown.png
index 3a6a2ed778..b5d9ffbbb4 100644
--- a/scene/resources/default_theme/dropdown.png
+++ b/scene/resources/default_theme/dropdown.png
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.png b/scene/resources/default_theme/error_icon.png
index f291362350..7741d00749 100644
--- a/scene/resources/default_theme/error_icon.png
+++ b/scene/resources/default_theme/error_icon.png
Binary files differ
diff --git a/scene/resources/default_theme/focus.png b/scene/resources/default_theme/focus.png
index 5d37028f2d..f51ea89e8f 100644
--- a/scene/resources/default_theme/focus.png
+++ b/scene/resources/default_theme/focus.png
Binary files differ
diff --git a/scene/resources/default_theme/frame_focus.png b/scene/resources/default_theme/frame_focus.png
index 9170db38ed..1b24ba47d8 100644
--- a/scene/resources/default_theme/frame_focus.png
+++ b/scene/resources/default_theme/frame_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/full_panel_bg.png b/scene/resources/default_theme/full_panel_bg.png
index 7f02dc7259..85f753cc13 100644
--- a/scene/resources/default_theme/full_panel_bg.png
+++ b/scene/resources/default_theme/full_panel_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_breakpoint.png b/scene/resources/default_theme/graph_node_breakpoint.png
index 0e36f31bd4..e18c6f42e1 100644
--- a/scene/resources/default_theme/graph_node_breakpoint.png
+++ b/scene/resources/default_theme/graph_node_breakpoint.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_close.png b/scene/resources/default_theme/graph_node_close.png
index 144a8b9c4c..5c962ae1c6 100644
--- a/scene/resources/default_theme/graph_node_close.png
+++ b/scene/resources/default_theme/graph_node_close.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment.png b/scene/resources/default_theme/graph_node_comment.png
index f2d6daa259..cdec1d1eac 100644
--- a/scene/resources/default_theme/graph_node_comment.png
+++ b/scene/resources/default_theme/graph_node_comment.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment_focus.png b/scene/resources/default_theme/graph_node_comment_focus.png
index a4b7b5a618..472a6b6f53 100644
--- a/scene/resources/default_theme/graph_node_comment_focus.png
+++ b/scene/resources/default_theme/graph_node_comment_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default.png b/scene/resources/default_theme/graph_node_default.png
index e3a220301f..359bbdc205 100644
--- a/scene/resources/default_theme/graph_node_default.png
+++ b/scene/resources/default_theme/graph_node_default.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default_focus.png b/scene/resources/default_theme/graph_node_default_focus.png
index 9972b07593..204dd16ac0 100644
--- a/scene/resources/default_theme/graph_node_default_focus.png
+++ b/scene/resources/default_theme/graph_node_default_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_position.png b/scene/resources/default_theme/graph_node_position.png
index 7ec15e2ff4..24c2759be6 100644
--- a/scene/resources/default_theme/graph_node_position.png
+++ b/scene/resources/default_theme/graph_node_position.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_selected.png b/scene/resources/default_theme/graph_node_selected.png
index f76c9703dd..cc4eb7f753 100644
--- a/scene/resources/default_theme/graph_node_selected.png
+++ b/scene/resources/default_theme/graph_node_selected.png
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.png b/scene/resources/default_theme/graph_port.png
index 9d5082cfdb..f33ae3baf3 100644
--- a/scene/resources/default_theme/graph_port.png
+++ b/scene/resources/default_theme/graph_port.png
Binary files differ
diff --git a/scene/resources/default_theme/hseparator.png b/scene/resources/default_theme/hseparator.png
index 99609ac118..d4fd71ace5 100644
--- a/scene/resources/default_theme/hseparator.png
+++ b/scene/resources/default_theme/hseparator.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_bg.png b/scene/resources/default_theme/hslider_bg.png
index 9c2a2df62a..b402bd370d 100644
--- a/scene/resources/default_theme/hslider_bg.png
+++ b/scene/resources/default_theme/hslider_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber.png b/scene/resources/default_theme/hslider_grabber.png
index 2acd33879a..d273b491ee 100644
--- a/scene/resources/default_theme/hslider_grabber.png
+++ b/scene/resources/default_theme/hslider_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_disabled.png b/scene/resources/default_theme/hslider_grabber_disabled.png
index 0d75182b8f..dddd1a468e 100644
--- a/scene/resources/default_theme/hslider_grabber_disabled.png
+++ b/scene/resources/default_theme/hslider_grabber_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_hl.png b/scene/resources/default_theme/hslider_grabber_hl.png
index f8a011e64b..e3defb3610 100644
--- a/scene/resources/default_theme/hslider_grabber_hl.png
+++ b/scene/resources/default_theme/hslider_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.png b/scene/resources/default_theme/hslider_tick.png
index f7afd78529..1ba19c37a1 100644
--- a/scene/resources/default_theme/hslider_tick.png
+++ b/scene/resources/default_theme/hslider_tick.png
Binary files differ
diff --git a/scene/resources/default_theme/hsplit_bg.png b/scene/resources/default_theme/hsplit_bg.png
index 7dd1d48b29..a5749f6d5c 100644
--- a/scene/resources/default_theme/hsplit_bg.png
+++ b/scene/resources/default_theme/hsplit_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.png b/scene/resources/default_theme/hsplitter.png
index 71a3914d7e..2287753c9d 100644
--- a/scene/resources/default_theme/hsplitter.png
+++ b/scene/resources/default_theme/hsplitter.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_add.png b/scene/resources/default_theme/icon_add.png
index fa675045bc..eccb69b363 100644
--- a/scene/resources/default_theme/icon_add.png
+++ b/scene/resources/default_theme/icon_add.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_close.png b/scene/resources/default_theme/icon_close.png
index 5ac6357dcd..4d4ac4a551 100644
--- a/scene/resources/default_theme/icon_close.png
+++ b/scene/resources/default_theme/icon_close.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_color_pick.png b/scene/resources/default_theme/icon_color_pick.png
index 15679a9558..46953febb8 100644
--- a/scene/resources/default_theme/icon_color_pick.png
+++ b/scene/resources/default_theme/icon_color_pick.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_folder.png b/scene/resources/default_theme/icon_folder.png
index cc05e98ebb..d1b308e88d 100644
--- a/scene/resources/default_theme/icon_folder.png
+++ b/scene/resources/default_theme/icon_folder.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_parent_folder.png b/scene/resources/default_theme/icon_parent_folder.png
index 47fee1ad81..35d218722e 100644
--- a/scene/resources/default_theme/icon_parent_folder.png
+++ b/scene/resources/default_theme/icon_parent_folder.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_play.png b/scene/resources/default_theme/icon_play.png
index 864e4e4fb9..b9ed6e6d5b 100644
--- a/scene/resources/default_theme/icon_play.png
+++ b/scene/resources/default_theme/icon_play.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_reload.png b/scene/resources/default_theme/icon_reload.png
index 9303fabb9c..bec5f3f4f9 100644
--- a/scene/resources/default_theme/icon_reload.png
+++ b/scene/resources/default_theme/icon_reload.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_snap_grid.png b/scene/resources/default_theme/icon_snap_grid.png
index 44db9bdfdc..0680317d86 100644
--- a/scene/resources/default_theme/icon_snap_grid.png
+++ b/scene/resources/default_theme/icon_snap_grid.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_stop.png b/scene/resources/default_theme/icon_stop.png
index 1f194d0e14..0c1371ceb9 100644
--- a/scene/resources/default_theme/icon_stop.png
+++ b/scene/resources/default_theme/icon_stop.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_less.png b/scene/resources/default_theme/icon_zoom_less.png
index 888ddc995d..03119c60ca 100644
--- a/scene/resources/default_theme/icon_zoom_less.png
+++ b/scene/resources/default_theme/icon_zoom_less.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_more.png b/scene/resources/default_theme/icon_zoom_more.png
index fa675045bc..31467ec3de 100644
--- a/scene/resources/default_theme/icon_zoom_more.png
+++ b/scene/resources/default_theme/icon_zoom_more.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_reset.png b/scene/resources/default_theme/icon_zoom_reset.png
index 953ae47d24..cac68c09fa 100644
--- a/scene/resources/default_theme/icon_zoom_reset.png
+++ b/scene/resources/default_theme/icon_zoom_reset.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit.png b/scene/resources/default_theme/line_edit.png
index bf2b91f1be..2b0c506f34 100644
--- a/scene/resources/default_theme/line_edit.png
+++ b/scene/resources/default_theme/line_edit.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_disabled.png b/scene/resources/default_theme/line_edit_disabled.png
index a0fa505e4c..69d78febd9 100644
--- a/scene/resources/default_theme/line_edit_disabled.png
+++ b/scene/resources/default_theme/line_edit_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_focus.png b/scene/resources/default_theme/line_edit_focus.png
index e66d7b60e3..1d74b74068 100644
--- a/scene/resources/default_theme/line_edit_focus.png
+++ b/scene/resources/default_theme/line_edit_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/logo.png b/scene/resources/default_theme/logo.png
index 2161402438..d0ef9d8aa7 100644
--- a/scene/resources/default_theme/logo.png
+++ b/scene/resources/default_theme/logo.png
Binary files differ
diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py
index db449f9417..73b1ae0b0b 100755
--- a/scene/resources/default_theme/make_header.py
+++ b/scene/resources/default_theme/make_header.py
@@ -4,15 +4,16 @@ import os
import glob
import string
+enc = "utf-8"
# Generate include files
f = open("theme_data.h", "wb")
-f.write("// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!\n")
+f.write(b"// THIS FILE HAS BEEN AUTOGENERATED, DON\'T EDIT!!\n")
# Generate png image block
-f.write("\n// png image block\n");
+f.write(b"\n// png image block\n")
pixmaps = glob.glob("*.png")
pixmaps.sort()
@@ -21,22 +22,23 @@ for x in pixmaps:
var_str = x[:-4] + "_png"
- f.write("\nstatic const unsigned char " + var_str + "[] = {\n\t")
+ s = "\nstatic const unsigned char " + var_str + "[] = {\n\t"
+ f.write(s.encode(enc))
pngf = open(x, "rb")
b = pngf.read(1)
while(len(b) == 1):
- f.write(hex(ord(b)))
+ f.write(hex(ord(b)).encode(enc))
b = pngf.read(1)
if (len(b) == 1):
- f.write(", ")
+ f.write(b", ")
- f.write("\n};\n")
+ f.write(b"\n};\n")
pngf.close()
# Generate shaders block
-f.write("\n// shaders block\n");
+f.write(b"\n// shaders block\n");
shaders = glob.glob("*.gsl")
shaders.sort()
@@ -45,7 +47,8 @@ for x in shaders:
var_str = x[:-4] + "_shader_code"
- f.write("\nstatic const char *" + var_str + " = \n")
+ s = "\nstatic const char *" + var_str + " = \n"
+ f.write(s.encode(enc))
sf = open(x, "rb")
@@ -55,12 +58,13 @@ for x in shaders:
b = b[:-2]
if (b.endswith("\n")):
b = b[:-1]
- f.write(" \"" + b)
+ s = ' \"' + b
+ f.write(s.encode(enc))
b = sf.readline()
if (b != ""):
- f.write("\"\n")
+ f.write(b'"\n')
- f.write("\";\n")
+ f.write(b'";\n')
sf.close()
f.close()
diff --git a/scene/resources/default_theme/mini_checkerboard.png b/scene/resources/default_theme/mini_checkerboard.png
index 3e53183847..d8279bda80 100644
--- a/scene/resources/default_theme/mini_checkerboard.png
+++ b/scene/resources/default_theme/mini_checkerboard.png
Binary files differ
diff --git a/scene/resources/default_theme/option_arrow.png b/scene/resources/default_theme/option_arrow.png
index 007de16bfa..40590fd60a 100644
--- a/scene/resources/default_theme/option_arrow.png
+++ b/scene/resources/default_theme/option_arrow.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_disabled.png b/scene/resources/default_theme/option_button_disabled.png
index ce727d56e1..1961b673cd 100644
--- a/scene/resources/default_theme/option_button_disabled.png
+++ b/scene/resources/default_theme/option_button_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_focus.png b/scene/resources/default_theme/option_button_focus.png
index c76d91287e..402670f9a2 100644
--- a/scene/resources/default_theme/option_button_focus.png
+++ b/scene/resources/default_theme/option_button_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover.png b/scene/resources/default_theme/option_button_hover.png
index fd1e987ceb..826fe1c9ca 100644
--- a/scene/resources/default_theme/option_button_hover.png
+++ b/scene/resources/default_theme/option_button_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal.png b/scene/resources/default_theme/option_button_normal.png
index 9d7fb98d1c..2dadb40338 100644
--- a/scene/resources/default_theme/option_button_normal.png
+++ b/scene/resources/default_theme/option_button_normal.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed.png b/scene/resources/default_theme/option_button_pressed.png
index 28b1d93468..68796f9d85 100644
--- a/scene/resources/default_theme/option_button_pressed.png
+++ b/scene/resources/default_theme/option_button_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/panel_bg.png b/scene/resources/default_theme/panel_bg.png
index 320819ad6d..b496e2177e 100644
--- a/scene/resources/default_theme/panel_bg.png
+++ b/scene/resources/default_theme/panel_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg.png b/scene/resources/default_theme/popup_bg.png
index 63f5994441..023029f936 100644
--- a/scene/resources/default_theme/popup_bg.png
+++ b/scene/resources/default_theme/popup_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg_disabled.png b/scene/resources/default_theme/popup_bg_disabled.png
index 611d949380..8eab5f27bc 100644
--- a/scene/resources/default_theme/popup_bg_disabled.png
+++ b/scene/resources/default_theme/popup_bg_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_checked.png b/scene/resources/default_theme/popup_checked.png
index a24e0543c0..b7b05640e1 100644
--- a/scene/resources/default_theme/popup_checked.png
+++ b/scene/resources/default_theme/popup_checked.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_hover.png b/scene/resources/default_theme/popup_hover.png
index 85d4e48475..bdb6ae8bd0 100644
--- a/scene/resources/default_theme/popup_hover.png
+++ b/scene/resources/default_theme/popup_hover.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_unchecked.png b/scene/resources/default_theme/popup_unchecked.png
index c1137e6fbf..ff922335c3 100644
--- a/scene/resources/default_theme/popup_unchecked.png
+++ b/scene/resources/default_theme/popup_unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_window.png b/scene/resources/default_theme/popup_window.png
index 59362a8ffd..174a29ef45 100644
--- a/scene/resources/default_theme/popup_window.png
+++ b/scene/resources/default_theme/popup_window.png
Binary files differ
diff --git a/scene/resources/default_theme/progress_bar.png b/scene/resources/default_theme/progress_bar.png
index bf81e3adea..057557e567 100644
--- a/scene/resources/default_theme/progress_bar.png
+++ b/scene/resources/default_theme/progress_bar.png
Binary files differ
diff --git a/scene/resources/default_theme/progress_fill.png b/scene/resources/default_theme/progress_fill.png
index 3a34dfdda6..e39bb2a021 100644
--- a/scene/resources/default_theme/progress_fill.png
+++ b/scene/resources/default_theme/progress_fill.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.png b/scene/resources/default_theme/radio_checked.png
index 95d472022f..0ce575c15f 100644
--- a/scene/resources/default_theme/radio_checked.png
+++ b/scene/resources/default_theme/radio_checked.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked.png b/scene/resources/default_theme/radio_unchecked.png
index 7f0535c3a4..fe5bcf6ab1 100644
--- a/scene/resources/default_theme/radio_unchecked.png
+++ b/scene/resources/default_theme/radio_unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/reference_border.png b/scene/resources/default_theme/reference_border.png
index 96219676bf..6a680f393c 100644
--- a/scene/resources/default_theme/reference_border.png
+++ b/scene/resources/default_theme/reference_border.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_bg.png b/scene/resources/default_theme/scroll_bg.png
index cefadb2c08..fb151a48b1 100644
--- a/scene/resources/default_theme/scroll_bg.png
+++ b/scene/resources/default_theme/scroll_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_down.png b/scene/resources/default_theme/scroll_button_down.png
index caeac9b286..1df4ef5b6b 100644
--- a/scene/resources/default_theme/scroll_button_down.png
+++ b/scene/resources/default_theme/scroll_button_down.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_down_hl.png b/scene/resources/default_theme/scroll_button_down_hl.png
index 48036e0297..ba79087393 100644
--- a/scene/resources/default_theme/scroll_button_down_hl.png
+++ b/scene/resources/default_theme/scroll_button_down_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.png b/scene/resources/default_theme/scroll_button_left.png
index 3b50938d97..e430cb4673 100644
--- a/scene/resources/default_theme/scroll_button_left.png
+++ b/scene/resources/default_theme/scroll_button_left.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left_hl.png b/scene/resources/default_theme/scroll_button_left_hl.png
index b3d348c24f..2a6ef17a34 100644
--- a/scene/resources/default_theme/scroll_button_left_hl.png
+++ b/scene/resources/default_theme/scroll_button_left_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right.png b/scene/resources/default_theme/scroll_button_right.png
index 1c622a41ad..4f61687aa4 100644
--- a/scene/resources/default_theme/scroll_button_right.png
+++ b/scene/resources/default_theme/scroll_button_right.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right_hl.png b/scene/resources/default_theme/scroll_button_right_hl.png
index 108796ca02..10e2722509 100644
--- a/scene/resources/default_theme/scroll_button_right_hl.png
+++ b/scene/resources/default_theme/scroll_button_right_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_up.png b/scene/resources/default_theme/scroll_button_up.png
index 2c8238ae4c..f425412f50 100644
--- a/scene/resources/default_theme/scroll_button_up.png
+++ b/scene/resources/default_theme/scroll_button_up.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_up_hl.png b/scene/resources/default_theme/scroll_button_up_hl.png
index 4283bd114a..615a236c52 100644
--- a/scene/resources/default_theme/scroll_button_up_hl.png
+++ b/scene/resources/default_theme/scroll_button_up_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber.png b/scene/resources/default_theme/scroll_grabber.png
index 1d625a9b7b..732725a28f 100644
--- a/scene/resources/default_theme/scroll_grabber.png
+++ b/scene/resources/default_theme/scroll_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_hl.png b/scene/resources/default_theme/scroll_grabber_hl.png
index 99eb24b7e7..006cfa3361 100644
--- a/scene/resources/default_theme/scroll_grabber_hl.png
+++ b/scene/resources/default_theme/scroll_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_pressed.png b/scene/resources/default_theme/scroll_grabber_pressed.png
index a46d242ddd..f4886158fa 100644
--- a/scene/resources/default_theme/scroll_grabber_pressed.png
+++ b/scene/resources/default_theme/scroll_grabber_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/selection.png b/scene/resources/default_theme/selection.png
index 501877a8b4..7d1c985b35 100644
--- a/scene/resources/default_theme/selection.png
+++ b/scene/resources/default_theme/selection.png
Binary files differ
diff --git a/scene/resources/default_theme/selection_oof.png b/scene/resources/default_theme/selection_oof.png
index 9594fe0913..2da0538389 100644
--- a/scene/resources/default_theme/selection_oof.png
+++ b/scene/resources/default_theme/selection_oof.png
Binary files differ
diff --git a/scene/resources/default_theme/spinbox_updown.png b/scene/resources/default_theme/spinbox_updown.png
index b40b1e9fd2..74fab19f34 100644
--- a/scene/resources/default_theme/spinbox_updown.png
+++ b/scene/resources/default_theme/spinbox_updown.png
Binary files differ
diff --git a/scene/resources/default_theme/submenu.png b/scene/resources/default_theme/submenu.png
index ec727eecf1..8f7de446d4 100644
--- a/scene/resources/default_theme/submenu.png
+++ b/scene/resources/default_theme/submenu.png
Binary files differ
diff --git a/scene/resources/default_theme/tab.png b/scene/resources/default_theme/tab.png
index 3e4d792a48..895daa65e2 100644
--- a/scene/resources/default_theme/tab.png
+++ b/scene/resources/default_theme/tab.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_behind.png b/scene/resources/default_theme/tab_behind.png
index 12f07c3a91..2803d9db65 100644
--- a/scene/resources/default_theme/tab_behind.png
+++ b/scene/resources/default_theme/tab_behind.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_close.png b/scene/resources/default_theme/tab_close.png
index 20d9b5c810..af2775a132 100644
--- a/scene/resources/default_theme/tab_close.png
+++ b/scene/resources/default_theme/tab_close.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_container_bg.png b/scene/resources/default_theme/tab_container_bg.png
index 92482aaf28..7d0bd16221 100644
--- a/scene/resources/default_theme/tab_container_bg.png
+++ b/scene/resources/default_theme/tab_container_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_current.png b/scene/resources/default_theme/tab_current.png
index 7289e032da..520d147217 100644
--- a/scene/resources/default_theme/tab_current.png
+++ b/scene/resources/default_theme/tab_current.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu.png b/scene/resources/default_theme/tab_menu.png
index 148b64b8aa..fa4421a28a 100644
--- a/scene/resources/default_theme/tab_menu.png
+++ b/scene/resources/default_theme/tab_menu.png
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu_hl.png b/scene/resources/default_theme/tab_menu_hl.png
index 148b64b8aa..fa4421a28a 100644
--- a/scene/resources/default_theme/tab_menu_hl.png
+++ b/scene/resources/default_theme/tab_menu_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off.png b/scene/resources/default_theme/toggle_off.png
index 71cd64b001..64b51c8c9d 100644
--- a/scene/resources/default_theme/toggle_off.png
+++ b/scene/resources/default_theme/toggle_off.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on.png b/scene/resources/default_theme/toggle_on.png
index 6ea1b589c7..f0c699c181 100644
--- a/scene/resources/default_theme/toggle_on.png
+++ b/scene/resources/default_theme/toggle_on.png
Binary files differ
diff --git a/scene/resources/default_theme/tool_button_pressed.png b/scene/resources/default_theme/tool_button_pressed.png
index bcf70b486d..5494475792 100644
--- a/scene/resources/default_theme/tool_button_pressed.png
+++ b/scene/resources/default_theme/tool_button_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/tooltip_bg.png b/scene/resources/default_theme/tooltip_bg.png
index eca0675a98..07b7d942ca 100644
--- a/scene/resources/default_theme/tooltip_bg.png
+++ b/scene/resources/default_theme/tooltip_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg.png b/scene/resources/default_theme/tree_bg.png
index 839a6a272a..2b0c506f34 100644
--- a/scene/resources/default_theme/tree_bg.png
+++ b/scene/resources/default_theme/tree_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_disabled.png b/scene/resources/default_theme/tree_bg_disabled.png
index a0fa505e4c..69d78febd9 100644
--- a/scene/resources/default_theme/tree_bg_disabled.png
+++ b/scene/resources/default_theme/tree_bg_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_focus.png b/scene/resources/default_theme/tree_bg_focus.png
index 692cf71926..aadc6b0db4 100644
--- a/scene/resources/default_theme/tree_bg_focus.png
+++ b/scene/resources/default_theme/tree_bg_focus.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_cursor.png b/scene/resources/default_theme/tree_cursor.png
index 94d2a08818..2b8722d066 100644
--- a/scene/resources/default_theme/tree_cursor.png
+++ b/scene/resources/default_theme/tree_cursor.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_cursor_unfocus.png b/scene/resources/default_theme/tree_cursor_unfocus.png
index 3f023bbabe..bfaebbea85 100644
--- a/scene/resources/default_theme/tree_cursor_unfocus.png
+++ b/scene/resources/default_theme/tree_cursor_unfocus.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_title.png b/scene/resources/default_theme/tree_title.png
index b0ddcffbbe..e5f3f49695 100644
--- a/scene/resources/default_theme/tree_title.png
+++ b/scene/resources/default_theme/tree_title.png
Binary files differ
diff --git a/scene/resources/default_theme/tree_title_pressed.png b/scene/resources/default_theme/tree_title_pressed.png
index 746d10039e..35e2bb3008 100644
--- a/scene/resources/default_theme/tree_title_pressed.png
+++ b/scene/resources/default_theme/tree_title_pressed.png
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.png b/scene/resources/default_theme/unchecked.png
index d6f790cbc2..8c818af755 100644
--- a/scene/resources/default_theme/unchecked.png
+++ b/scene/resources/default_theme/unchecked.png
Binary files differ
diff --git a/scene/resources/default_theme/updown.png b/scene/resources/default_theme/updown.png
index 916284a3cf..56f81921e8 100644
--- a/scene/resources/default_theme/updown.png
+++ b/scene/resources/default_theme/updown.png
Binary files differ
diff --git a/scene/resources/default_theme/vseparator.png b/scene/resources/default_theme/vseparator.png
index 498768c05b..51e79f3ac5 100644
--- a/scene/resources/default_theme/vseparator.png
+++ b/scene/resources/default_theme/vseparator.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_bg.png b/scene/resources/default_theme/vslider_bg.png
index 8d9ead3c5a..ba3244e3e5 100644
--- a/scene/resources/default_theme/vslider_bg.png
+++ b/scene/resources/default_theme/vslider_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber.png b/scene/resources/default_theme/vslider_grabber.png
index afc490be45..6c6bf93e68 100644
--- a/scene/resources/default_theme/vslider_grabber.png
+++ b/scene/resources/default_theme/vslider_grabber.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_disabled.png b/scene/resources/default_theme/vslider_grabber_disabled.png
index c830359f45..49cced5055 100644
--- a/scene/resources/default_theme/vslider_grabber_disabled.png
+++ b/scene/resources/default_theme/vslider_grabber_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_hl.png b/scene/resources/default_theme/vslider_grabber_hl.png
index 548972e115..28774fdbf8 100644
--- a/scene/resources/default_theme/vslider_grabber_hl.png
+++ b/scene/resources/default_theme/vslider_grabber_hl.png
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.png b/scene/resources/default_theme/vslider_tick.png
index 873ebb00d8..bde788b563 100644
--- a/scene/resources/default_theme/vslider_tick.png
+++ b/scene/resources/default_theme/vslider_tick.png
Binary files differ
diff --git a/scene/resources/default_theme/vsplit_bg.png b/scene/resources/default_theme/vsplit_bg.png
index 7dd1d48b29..a5749f6d5c 100644
--- a/scene/resources/default_theme/vsplit_bg.png
+++ b/scene/resources/default_theme/vsplit_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.png b/scene/resources/default_theme/vsplitter.png
index ec5542bf69..dde1f390df 100644
--- a/scene/resources/default_theme/vsplitter.png
+++ b/scene/resources/default_theme/vsplitter.png
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer.png b/scene/resources/default_theme/window_resizer.png
index ed51968c4e..b06e6f5366 100644
--- a/scene/resources/default_theme/window_resizer.png
+++ b/scene/resources/default_theme/window_resizer.png
Binary files differ
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index 05493d5777..e5d463d391 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -625,7 +625,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
break;
}
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
if (error) {
char_map[p_char] = character;
return;
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 3fab4d3cfc..d3da842b79 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -378,7 +378,7 @@ bool Environment::is_ssr_rough() const {
void Environment::set_ssao_enabled(bool p_enable) {
ssao_enabled = p_enable;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
_change_notify();
}
@@ -390,7 +390,7 @@ bool Environment::is_ssao_enabled() const {
void Environment::set_ssao_radius(float p_radius) {
ssao_radius = p_radius;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_radius() const {
@@ -400,7 +400,7 @@ float Environment::get_ssao_radius() const {
void Environment::set_ssao_intensity(float p_intensity) {
ssao_intensity = p_intensity;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_intensity() const {
@@ -411,7 +411,7 @@ float Environment::get_ssao_intensity() const {
void Environment::set_ssao_radius2(float p_radius) {
ssao_radius2 = p_radius;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_radius2() const {
@@ -421,7 +421,7 @@ float Environment::get_ssao_radius2() const {
void Environment::set_ssao_intensity2(float p_intensity) {
ssao_intensity2 = p_intensity;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_intensity2() const {
@@ -431,7 +431,7 @@ float Environment::get_ssao_intensity2() const {
void Environment::set_ssao_bias(float p_bias) {
ssao_bias = p_bias;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_bias() const {
@@ -441,17 +441,27 @@ float Environment::get_ssao_bias() const {
void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) {
ssao_direct_light_affect = p_direct_light_affect;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_direct_light_affect() const {
return ssao_direct_light_affect;
}
+void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) {
+
+ ssao_ao_channel_affect = p_ao_channel_affect;
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+}
+float Environment::get_ssao_ao_channel_affect() const {
+
+ return ssao_ao_channel_affect;
+}
+
void Environment::set_ssao_color(const Color &p_color) {
ssao_color = p_color;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Color Environment::get_ssao_color() const {
@@ -462,7 +472,7 @@ Color Environment::get_ssao_color() const {
void Environment::set_ssao_blur(SSAOBlur p_blur) {
ssao_blur = p_blur;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Environment::SSAOBlur Environment::get_ssao_blur() const {
@@ -472,7 +482,7 @@ Environment::SSAOBlur Environment::get_ssao_blur() const {
void Environment::set_ssao_quality(SSAOQuality p_quality) {
ssao_quality = p_quality;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
Environment::SSAOQuality Environment::get_ssao_quality() const {
@@ -483,7 +493,7 @@ Environment::SSAOQuality Environment::get_ssao_quality() const {
void Environment::set_ssao_edge_sharpness(float p_edge_sharpness) {
ssao_edge_sharpness = p_edge_sharpness;
- VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+ VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
}
float Environment::get_ssao_edge_sharpness() const {
@@ -1008,6 +1018,9 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ssao_direct_light_affect", "amount"), &Environment::set_ssao_direct_light_affect);
ClassDB::bind_method(D_METHOD("get_ssao_direct_light_affect"), &Environment::get_ssao_direct_light_affect);
+ ClassDB::bind_method(D_METHOD("set_ssao_ao_channel_affect", "amount"), &Environment::set_ssao_ao_channel_affect);
+ ClassDB::bind_method(D_METHOD("get_ssao_ao_channel_affect"), &Environment::get_ssao_ao_channel_affect);
+
ClassDB::bind_method(D_METHOD("set_ssao_color", "color"), &Environment::set_ssao_color);
ClassDB::bind_method(D_METHOD("get_ssao_color"), &Environment::get_ssao_color);
@@ -1028,6 +1041,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_intensity2", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_intensity2", "get_ssao_intensity2");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ssao_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ssao_color", "get_ssao_color");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_ssao_quality", "get_ssao_quality");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur");
@@ -1220,6 +1234,7 @@ Environment::Environment() {
ssao_intensity2 = 1;
ssao_bias = 0.01;
ssao_direct_light_affect = 0.0;
+ ssao_ao_channel_affect = 0.0;
ssao_blur = SSAO_BLUR_3x3;
set_ssao_edge_sharpness(4);
set_ssao_quality(SSAO_QUALITY_LOW);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 27fd57aa09..7d66c7e60b 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -127,6 +127,7 @@ private:
float ssao_intensity2;
float ssao_bias;
float ssao_direct_light_affect;
+ float ssao_ao_channel_affect;
Color ssao_color;
SSAOBlur ssao_blur;
float ssao_edge_sharpness;
@@ -274,6 +275,9 @@ public:
void set_ssao_direct_light_affect(float p_direct_light_affect);
float get_ssao_direct_light_affect() const;
+ void set_ssao_ao_channel_affect(float p_ao_channel_affect);
+ float get_ssao_ao_channel_affect() const;
+
void set_ssao_color(const Color &p_color);
Color get_ssao_color() const;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 5e7569586a..56b3ee70bb 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -145,11 +145,17 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
+ if (shader.is_valid()) {
+ shader->disconnect("changed", this, "_shader_changed");
+ }
+
shader = p_shader;
RID rid;
- if (shader.is_valid())
+ if (shader.is_valid()) {
rid = shader->get_rid();
+ shader->connect("changed", this, "_shader_changed");
+ }
VS::get_singleton()->material_set_shader(_get_material(), rid);
_change_notify(); //properties for shader exposed
@@ -171,12 +177,17 @@ Variant ShaderMaterial::get_shader_param(const StringName &p_param) const {
return VS::get_singleton()->material_get_param(_get_material(), p_param);
}
+void ShaderMaterial::_shader_changed() {
+ _change_notify(); //update all properties
+}
+
void ShaderMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader);
ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader);
ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param);
ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param);
+ ClassDB::bind_method(D_METHOD("_shader_changed"), &ShaderMaterial::_shader_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader");
}
@@ -393,6 +404,12 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
code += ",shadows_disabled";
}
+ if (flags[FLAG_DISABLE_AMBIENT_LIGHT]) {
+ code += ",ambient_light_disabled";
+ }
+ if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
+ code += ",ensure_correct_normals";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -1852,6 +1869,8 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_ensure_correct_normals"), "set_flag", "get_flag", FLAG_ENSURE_CORRECT_NORMALS);
ADD_GROUP("Vertex Color", "vertex_color");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR);
@@ -2042,6 +2061,8 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT);
+ BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index ce733bfb8d..84f1005b03 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -92,6 +92,8 @@ protected:
virtual bool _can_do_next_pass() const;
+ void _shader_changed();
+
public:
void set_shader(const Ref<Shader> &p_shader);
Ref<Shader> get_shader() const;
@@ -189,6 +191,8 @@ public:
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_DONT_RECEIVE_SHADOWS,
+ FLAG_ENSURE_CORRECT_NORMALS,
+ FLAG_DISABLE_AMBIENT_LIGHT,
FLAG_MAX
};
@@ -237,7 +241,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 15;
+ uint64_t flags : 17;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index b0620d3363..fe87dcdd2c 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -40,7 +40,6 @@
void Mesh::_clear_triangle_mesh() const {
triangle_mesh.unref();
- ;
}
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
@@ -110,6 +109,54 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
return triangle_mesh;
}
+void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) {
+
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<int> triangle_indices;
+ tm->get_indices(&triangle_indices);
+ const int triangles_num = tm->get_triangles().size();
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ r_lines.resize(tm->get_triangles().size() * 6); // 3 lines x 2 points each line
+
+ PoolVector<int>::Read ind_r = triangle_indices.read();
+ PoolVector<Vector3>::Read ver_r = vertices.read();
+ for (int j = 0, x = 0, i = 0; i < triangles_num; j += 6, x += 3, ++i) {
+ // Triangle line 1
+ r_lines[j + 0] = ver_r[ind_r[x + 0]];
+ r_lines[j + 1] = ver_r[ind_r[x + 1]];
+
+ // Triangle line 2
+ r_lines[j + 2] = ver_r[ind_r[x + 1]];
+ r_lines[j + 3] = ver_r[ind_r[x + 2]];
+
+ // Triangle line 3
+ r_lines[j + 4] = ver_r[ind_r[x + 2]];
+ r_lines[j + 5] = ver_r[ind_r[x + 0]];
+ }
+}
+void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ int vertices_size = vertices.size();
+ r_points.resize(vertices_size);
+ for (int i = 0; i < vertices_size; ++i) {
+ r_points[i] = vertices[i];
+ }
+}
+
+bool Mesh::surface_is_softbody_friendly(int p_idx) const {
+ const uint32_t surface_format = surface_get_format(p_idx);
+ return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_VERTEX)) && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL)));
+}
+
PoolVector<Face3> Mesh::get_faces() const {
Ref<TriangleMesh> tm = generate_triangle_mesh();
@@ -484,6 +531,10 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_MAX);
}
+void Mesh::clear_cache() {
+ _clear_triangle_mesh();
+}
+
Mesh::Mesh() {
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index e8b7ecaf9a..2127eaae4c 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -100,7 +100,7 @@ public:
ARRAY_FLAG_USE_16_BIT_BONES = ARRAY_COMPRESS_INDEX << 2,
ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_VERTEX | ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
+ ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
};
@@ -123,6 +123,7 @@ public:
virtual int get_surface_count() const = 0;
virtual int surface_get_array_len(int p_idx) const = 0;
virtual int surface_get_array_index_len(int p_idx) const = 0;
+ virtual bool surface_is_softbody_friendly(int p_idx) const;
virtual Array surface_get_arrays(int p_surface) const = 0;
virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0;
virtual uint32_t surface_get_format(int p_idx) const = 0;
@@ -133,6 +134,8 @@ public:
PoolVector<Face3> get_faces() const;
Ref<TriangleMesh> generate_triangle_mesh() const;
+ void generate_debug_mesh_lines(Vector<Vector3> &r_lines);
+ void generate_debug_mesh_indices(Vector<Vector3> &r_points);
Ref<Shape> create_trimesh_shape() const;
Ref<Shape> create_convex_shape() const;
@@ -143,6 +146,7 @@ public:
void set_lightmap_size_hint(const Vector2 &p_size);
Size2 get_lightmap_size_hint() const;
+ void clear_cache();
Mesh();
};
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
new file mode 100644
index 0000000000..de3cfd1371
--- /dev/null
+++ b/scene/resources/physics_material.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* physics_material.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "physics_material.h"
+
+bool PhysicsMaterial::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "bounce") {
+ set_bounce(p_value);
+ } else if (p_name == "bounce_combine_mode") {
+ set_bounce_combine_mode(static_cast<PhysicsServer::CombineMode>(int(p_value)));
+ } else if (p_name == "friction") {
+ set_friction(p_value);
+ } else if (p_name == "friction_combine_mode") {
+ set_friction_combine_mode(static_cast<PhysicsServer::CombineMode>(int(p_value)));
+ } else {
+ return false;
+ }
+
+ emit_changed();
+ return true;
+}
+
+bool PhysicsMaterial::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "bounce") {
+ r_ret = bounce;
+ } else if (p_name == "bounce_combine_mode") {
+ r_ret = int(bounce_combine_mode);
+ } else if (p_name == "friction") {
+ r_ret = friction;
+ } else if (p_name == "friction_combine_mode") {
+ r_ret = int(friction_combine_mode);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicsMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::REAL, "bounce"));
+ p_list->push_back(PropertyInfo(Variant::INT, "bounce_combine_mode", PROPERTY_HINT_ENUM, "Max,Min,Multiply,Average"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "friction"));
+ p_list->push_back(PropertyInfo(Variant::INT, "friction_combine_mode", PROPERTY_HINT_ENUM, "Max,Min,Multiply,Average"));
+}
+
+void PhysicsMaterial::_bind_methods() {}
+
+void PhysicsMaterial::set_bounce(real_t p_val) {
+ bounce = p_val;
+}
+
+void PhysicsMaterial::set_bounce_combine_mode(PhysicsServer::CombineMode p_val) {
+ bounce_combine_mode = p_val;
+}
+
+void PhysicsMaterial::set_friction(real_t p_val) {
+ friction = p_val;
+}
+
+void PhysicsMaterial::set_friction_combine_mode(PhysicsServer::CombineMode p_val) {
+ friction_combine_mode = p_val;
+}
+
+PhysicsMaterial::PhysicsMaterial() :
+ bounce(0),
+ bounce_combine_mode(PhysicsServer::COMBINE_MODE_MAX),
+ friction(0),
+ friction_combine_mode(PhysicsServer::COMBINE_MODE_MULTIPLY) {}
diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h
new file mode 100644
index 0000000000..a6cb8c288e
--- /dev/null
+++ b/scene/resources/physics_material.h
@@ -0,0 +1,70 @@
+/*************************************************************************/
+/* physics_material.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef physics_material_override_H
+#define physics_material_override_H
+
+#include "resource.h"
+#include "servers/physics_server.h"
+
+class PhysicsMaterial : public Resource {
+
+ GDCLASS(PhysicsMaterial, Resource);
+ OBJ_SAVE_TYPE(PhysicsMaterial);
+ RES_BASE_EXTENSION("PhyMat");
+
+ real_t bounce;
+ PhysicsServer::CombineMode bounce_combine_mode;
+ real_t friction;
+ PhysicsServer::CombineMode friction_combine_mode;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+ void set_bounce(real_t p_val);
+ _FORCE_INLINE_ real_t get_bounce() const { return bounce; }
+
+ void set_bounce_combine_mode(PhysicsServer::CombineMode p_val);
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_bounce_combine_mode() const { return bounce_combine_mode; }
+
+ void set_friction(real_t p_val);
+ _FORCE_INLINE_ real_t get_friction() const { return friction; }
+
+ void set_friction_combine_mode(PhysicsServer::CombineMode p_val);
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_friction_combine_mode() const { return friction_combine_mode; }
+
+ PhysicsMaterial();
+};
+
+#endif // physics_material_override_H
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index 597866eb74..9df117d09c 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -896,7 +896,7 @@ static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p
CharString utf8 = p_string.utf8();
if (p_bit_on_len) {
- f->store_32(utf8.length() + 1 | 0x80000000);
+ f->store_32((utf8.length() + 1) | 0x80000000);
} else {
f->store_32(utf8.length() + 1);
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 36740a307b..f53f03c1c8 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -54,16 +54,20 @@ void Shader::set_code(const String &p_code) {
VisualServer::get_singleton()->shader_set_code(shader, p_code);
params_cache_dirty = true;
- emit_signal(SceneStringNames::get_singleton()->changed);
+
+ emit_changed();
}
String Shader::get_code() const {
+ _update_shader();
return VisualServer::get_singleton()->shader_get_code(shader);
}
void Shader::get_param_list(List<PropertyInfo> *p_params) const {
+ _update_shader();
+
List<PropertyInfo> local;
VisualServer::get_singleton()->shader_get_param_list(shader, &local);
params_cache.clear();
@@ -72,6 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
for (List<PropertyInfo>::Element *E = local.front(); E; E = E->next()) {
PropertyInfo pi = E->get();
+ if (default_textures.has(pi.name)) { //do not show default textures
+ continue;
+ }
pi.name = "shader_param/" + pi.name;
params_cache[pi.name] = E->get().name;
if (p_params) {
@@ -86,6 +93,8 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
RID Shader::get_rid() const {
+ _update_shader();
+
return shader;
}
@@ -98,6 +107,8 @@ void Shader::set_default_texture_param(const StringName &p_param, const Ref<Text
default_textures.erase(p_param);
VS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID());
}
+
+ emit_changed();
}
Ref<Texture> Shader::get_default_texture_param(const StringName &p_param) const {
@@ -120,6 +131,9 @@ bool Shader::has_param(const StringName &p_param) const {
return params_cache.has(p_param);
}
+void Shader::_update_shader() const {
+}
+
void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mode"), &Shader::get_mode);
@@ -227,5 +241,5 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource,
}
bool ResourceFormatSaverShader::recognize(const RES &p_resource) const {
- return Object::cast_to<Shader>(*p_resource) != NULL;
+ return p_resource->get_class_name() == "Shader"; //only shader, not inherited
}
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 248a6f0125..efc5da7753 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -61,12 +61,13 @@ private:
mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
Map<StringName, Ref<Texture> > default_textures;
+ virtual void _update_shader() const; //used for visual shader
protected:
static void _bind_methods();
public:
//void set_mode(Mode p_mode);
- Mode get_mode() const;
+ virtual Mode get_mode() const;
void set_code(const String &p_code);
String get_code() const;
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index ebad00b068..fb81375b0a 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -905,12 +905,20 @@ bool StyleBoxLine::is_vertical() const {
return vertical;
}
-void StyleBoxLine::set_grow(float p_grow) {
- grow = p_grow;
+void StyleBoxLine::set_grow_end(float p_grow_end) {
+ grow_end = p_grow_end;
emit_changed();
}
-float StyleBoxLine::get_grow() const {
- return grow;
+float StyleBoxLine::get_grow_end() const {
+ return grow_end;
+}
+
+void StyleBoxLine::set_grow_begin(float p_grow_begin) {
+ grow_begin = p_grow_begin;
+ emit_changed();
+}
+float StyleBoxLine::get_grow_begin() const {
+ return grow_begin;
}
void StyleBoxLine::_bind_methods() {
@@ -919,13 +927,16 @@ void StyleBoxLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color);
ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness);
ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness);
- ClassDB::bind_method(D_METHOD("set_grow", "grow"), &StyleBoxLine::set_grow);
- ClassDB::bind_method(D_METHOD("get_grow"), &StyleBoxLine::get_grow);
+ ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin);
+ ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin);
+ ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end);
+ ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end);
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical);
ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow", "get_grow");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_begin", "get_grow_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_end", "get_grow_end");
ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
}
@@ -941,12 +952,12 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Rect2i r = p_rect;
if (vertical) {
- r.position.y -= grow;
- r.size.y += grow * 2;
+ r.position.y -= grow_begin;
+ r.size.y += (grow_begin + grow_end);
r.size.x = thickness;
} else {
- r.position.x -= grow;
- r.size.x += grow * 2;
+ r.position.x -= grow_begin;
+ r.size.x += (grow_begin + grow_end);
r.size.y = thickness;
}
@@ -954,7 +965,8 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
}
StyleBoxLine::StyleBoxLine() {
- grow = 1.0;
+ grow_begin = 1.0;
+ grow_end = 1.0;
thickness = 1;
color = Color(0.0, 0.0, 0.0);
vertical = false;
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index c1d84fe19f..ed193a1ab4 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -236,7 +236,8 @@ class StyleBoxLine : public StyleBox {
Color color;
int thickness;
bool vertical;
- float grow;
+ float grow_begin;
+ float grow_end;
protected:
virtual float get_style_margin(Margin p_margin) const;
@@ -252,8 +253,11 @@ public:
void set_vertical(bool p_vertical);
bool is_vertical() const;
- void set_grow(float p_grow);
- float get_grow() const;
+ void set_grow_begin(float p_grow);
+ float get_grow_begin() const;
+
+ void set_grow_end(float p_grow);
+ float get_grow_end() const;
virtual Size2 get_center_size() const;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 5a42873d79..3df9ab26a4 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -421,6 +421,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_
Array a = commit_to_arrays();
mesh->add_surface_from_arrays(primitive, a, Array(), p_flags);
+
if (material.is_valid())
mesh->surface_set_material(surface, material);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 54f5aea160..2baad555c0 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1669,3 +1669,208 @@ ProxyTexture::~ProxyTexture() {
VS::get_singleton()->free(proxy);
}
+//////////////////////////////////////////////
+
+void AnimatedTexture::_update_proxy() {
+
+ _THREAD_SAFE_METHOD_
+
+ float delta;
+ if (prev_ticks == 0) {
+ delta = 0;
+ prev_ticks = OS::get_singleton()->get_ticks_usec();
+ } else {
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ delta = float(double(ticks - prev_ticks) / 1000000.0);
+ prev_ticks = ticks;
+ }
+
+ time += delta;
+
+ float limit;
+
+ if (fps == 0) {
+ limit = 0;
+ } else {
+ limit = 1.0 / fps;
+ }
+
+ int iter_max = frame_count;
+ while (iter_max) {
+ float frame_limit = limit + frames[current_frame].delay_sec;
+
+ if (time > frame_limit) {
+ current_frame++;
+ if (current_frame >= frame_count) {
+ current_frame = 0;
+ }
+ time -= frame_limit;
+ } else {
+ break;
+ }
+ iter_max--;
+ }
+
+ if (frames[current_frame].texture.is_valid()) {
+ VisualServer::get_singleton()->texture_set_proxy(proxy, frames[current_frame].texture->get_rid());
+ }
+}
+
+void AnimatedTexture::set_frames(int p_frames) {
+ ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frame_count = p_frames;
+}
+int AnimatedTexture::get_frames() const {
+ return frame_count;
+}
+
+void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture> &p_texture) {
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frames[p_frame].texture = p_texture;
+}
+Ref<Texture> AnimatedTexture::get_frame_texture(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture>());
+
+ _THREAD_SAFE_METHOD_
+
+ return frames[p_frame].texture;
+}
+
+void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) {
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ _THREAD_SAFE_METHOD_
+
+ frames[p_frame].delay_sec = p_delay_sec;
+}
+float AnimatedTexture::get_frame_delay(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0);
+
+ _THREAD_SAFE_METHOD_
+
+ return frames[p_frame].delay_sec;
+}
+
+void AnimatedTexture::set_fps(float p_fps) {
+ ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000);
+
+ fps = p_fps;
+}
+float AnimatedTexture::get_fps() const {
+ return fps;
+}
+
+int AnimatedTexture::get_width() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_width();
+}
+int AnimatedTexture::get_height() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_height();
+}
+RID AnimatedTexture::get_rid() const {
+ return proxy;
+}
+
+bool AnimatedTexture::has_alpha() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return false;
+ }
+
+ return frames[current_frame].texture->has_alpha();
+}
+
+Ref<Image> AnimatedTexture::get_data() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return Ref<Image>();
+ }
+
+ return frames[current_frame].texture->get_data();
+}
+
+void AnimatedTexture::set_flags(uint32_t p_flags) {
+}
+uint32_t AnimatedTexture::get_flags() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 0;
+ }
+
+ return frames[current_frame].texture->get_flags();
+}
+
+void AnimatedTexture::_validate_property(PropertyInfo &property) const {
+
+ String prop = property.name;
+ if (prop.begins_with("frame_")) {
+ int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ if (frame >= frame_count) {
+ property.usage = 0;
+ }
+ }
+}
+
+void AnimatedTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames);
+ ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames);
+
+ ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps);
+ ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps);
+
+ ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture);
+ ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture);
+
+ ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay);
+ ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay);
+
+ ClassDB::bind_method(D_METHOD("_update_proxy"), &AnimatedTexture::_update_proxy);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps");
+
+ for (int i = 0; i < MAX_FRAMES; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i);
+ }
+}
+
+AnimatedTexture::AnimatedTexture() {
+ proxy = VS::get_singleton()->texture_create();
+ VisualServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true);
+ time = 0;
+ frame_count = 1;
+ fps = 4;
+ prev_ticks = 0;
+ current_frame = 0;
+ VisualServer::get_singleton()->connect("frame_pre_draw", this, "_update_proxy");
+}
+
+AnimatedTexture::~AnimatedTexture() {
+ VS::get_singleton()->free(proxy);
+}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index d81fd3b19b..c994bdad5f 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -34,10 +34,11 @@
#include "curve.h"
#include "io/resource_loader.h"
#include "math_2d.h"
+#include "os/mutex.h"
+#include "os/thread_safe.h"
#include "resource.h"
#include "scene/resources/color_ramp.h"
#include "servers/visual_server.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -521,4 +522,70 @@ public:
~ProxyTexture();
};
+class AnimatedTexture : public Texture {
+ GDCLASS(AnimatedTexture, Texture)
+
+ _THREAD_SAFE_CLASS_
+
+private:
+ enum {
+ MAX_FRAMES = 256
+ };
+
+ RID proxy;
+
+ struct Frame {
+
+ Ref<Texture> texture;
+ float delay_sec;
+
+ Frame() {
+ delay_sec = 0;
+ }
+ };
+
+ Frame frames[MAX_FRAMES];
+ int frame_count;
+ int current_frame;
+
+ float fps;
+
+ float time;
+
+ uint64_t prev_ticks;
+
+ void _update_proxy();
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ void set_frames(int p_frames);
+ int get_frames() const;
+
+ void set_frame_texture(int p_frame, const Ref<Texture> &p_texture);
+ Ref<Texture> get_frame_texture(int p_frame) const;
+
+ void set_frame_delay(int p_frame, float p_delay_sec);
+ float get_frame_delay(int p_frame) const;
+
+ void set_fps(float p_fps);
+ float get_fps() const;
+
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual RID get_rid() const;
+
+ virtual bool has_alpha() const;
+
+ virtual void set_flags(uint32_t p_flags);
+ virtual uint32_t get_flags() const;
+
+ virtual Ref<Image> get_data() const;
+
+ AnimatedTexture();
+ ~AnimatedTexture();
+};
+
#endif
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index c23f237c75..e0d4038e7e 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -55,12 +55,12 @@ class Theme : public Resource {
void _unref_font(Ref<Font> p_sc);
void _emit_theme_changed();
- HashMap<StringName, HashMap<StringName, Ref<Texture>, StringNameHasher>, StringNameHasher> icon_map;
- HashMap<StringName, HashMap<StringName, Ref<StyleBox>, StringNameHasher>, StringNameHasher> style_map;
- HashMap<StringName, HashMap<StringName, Ref<Font>, StringNameHasher>, StringNameHasher> font_map;
- HashMap<StringName, HashMap<StringName, Ref<Shader>, StringNameHasher>, StringNameHasher> shader_map;
- HashMap<StringName, HashMap<StringName, Color, StringNameHasher>, StringNameHasher> color_map;
- HashMap<StringName, HashMap<StringName, int, StringNameHasher>, StringNameHasher> constant_map;
+ HashMap<StringName, HashMap<StringName, Ref<Texture> > > icon_map;
+ HashMap<StringName, HashMap<StringName, Ref<StyleBox> > > style_map;
+ HashMap<StringName, HashMap<StringName, Ref<Font> > > font_map;
+ HashMap<StringName, HashMap<StringName, Ref<Shader> > > shader_map;
+ HashMap<StringName, HashMap<StringName, Color> > color_map;
+ HashMap<StringName, HashMap<StringName, int> > constant_map;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
new file mode 100644
index 0000000000..b7b7802d1b
--- /dev/null
+++ b/scene/resources/visual_shader.cpp
@@ -0,0 +1,1555 @@
+#include "visual_shader.h"
+#include "servers/visual/shader_types.h"
+#include "vmap.h"
+
+void VisualShaderNode::set_output_port_for_preview(int p_index) {
+
+ port_preview = p_index;
+}
+
+int VisualShaderNode::get_output_port_for_preview() const {
+
+ return port_preview;
+}
+
+void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) {
+ default_input_values[p_port] = p_value;
+ emit_changed();
+}
+
+Variant VisualShaderNode::get_input_port_default_value(int p_port) const {
+ if (default_input_values.has(p_port)) {
+ return default_input_values[p_port];
+ }
+
+ return Variant();
+}
+
+bool VisualShaderNode::is_port_separator(int p_index) const {
+ return false;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ return Vector<VisualShader::DefaultTextureParam>();
+}
+String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return String();
+}
+
+Vector<StringName> VisualShaderNode::get_editable_properties() const {
+ return Vector<StringName>();
+}
+
+Array VisualShaderNode::_get_default_input_values() const {
+
+ Array ret;
+ for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ ret.push_back(E->get());
+ }
+ return ret;
+}
+void VisualShaderNode::_set_default_input_values(const Array &p_values) {
+
+ if (p_values.size() % 2 == 0) {
+ for (int i = 0; i < p_values.size(); i += 2) {
+ default_input_values[p_values[i + 0]] = p_values[i + 1];
+ }
+ }
+
+ emit_changed();
+}
+
+String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ return String();
+}
+
+void VisualShaderNode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview);
+ ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview);
+
+ ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
+
+ ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualShaderNode::_set_default_input_values);
+ ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualShaderNode::_get_default_input_values);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values");
+ ADD_SIGNAL(MethodInfo("editor_refresh_request"));
+}
+
+VisualShaderNode::VisualShaderNode() {
+ port_preview = -1;
+}
+
+/////////////////////////////////////////////////////////
+
+void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) {
+ ERR_FAIL_COND(p_node.is_null());
+ ERR_FAIL_COND(p_id < 2);
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(g->nodes.has(p_id));
+ Node n;
+ n.node = p_node;
+ n.position = p_position;
+
+ Ref<VisualShaderNodeUniform> uniform = n.node;
+ if (uniform.is_valid()) {
+ String valid_name = validate_uniform_name(uniform->get_uniform_name(), uniform);
+ uniform->set_uniform_name(valid_name);
+ }
+
+ Ref<VisualShaderNodeInput> input = n.node;
+ if (input.is_valid()) {
+ input->shader_mode = shader_mode;
+ input->shader_type = p_type;
+ input->connect("input_type_changed", this, "_input_type_changed", varray(p_type, p_id));
+ }
+
+ n.node->connect("changed", this, "_queue_update");
+
+ g->nodes[p_id] = n;
+
+ _queue_update();
+}
+
+void VisualShader::set_node_position(Type p_type, int p_id, const Vector2 &p_position) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(!g->nodes.has(p_id));
+ g->nodes[p_id].position = p_position;
+}
+
+Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector2());
+ const Graph *g = &graph[p_type];
+ ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2());
+ return g->nodes[p_id].position;
+}
+Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>());
+ const Graph *g = &graph[p_type];
+ ERR_FAIL_COND_V(!g->nodes.has(p_id), Ref<VisualShaderNode>());
+ return g->nodes[p_id].node;
+}
+
+Vector<int> VisualShader::get_node_list(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector<int>());
+ const Graph *g = &graph[p_type];
+
+ Vector<int> ret;
+ for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+
+ return ret;
+}
+int VisualShader::get_valid_node_id(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, NODE_ID_INVALID);
+ const Graph *g = &graph[p_type];
+ return g->nodes.size() ? MAX(2, g->nodes.back()->key() + 1) : 2;
+}
+
+int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const {
+ for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) {
+ if (E->get().node == p_node)
+ return E->key();
+ }
+
+ return NODE_ID_INVALID;
+}
+
+void VisualShader::remove_node(Type p_type, int p_id) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ ERR_FAIL_COND(p_id < 2);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(!g->nodes.has(p_id));
+
+ Ref<VisualShaderNodeInput> input = g->nodes[p_id].node;
+ if (input.is_valid()) {
+ input->disconnect("input_type_changed", this, "_input_type_changed");
+ }
+
+ g->nodes[p_id].node->disconnect("changed", this, "_queue_update");
+
+ g->nodes.erase(p_id);
+
+ for (List<Connection>::Element *E = g->connections.front(); E;) {
+ List<Connection>::Element *N = E->next();
+ if (E->get().from_node == p_id || E->get().to_node == p_id) {
+ g->connections.erase(E);
+ }
+ E = N;
+ }
+
+ _queue_update();
+}
+
+bool VisualShader::is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
+ const Graph *g = &graph[p_type];
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
+
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
+ const Graph *g = &graph[p_type];
+
+ if (!g->nodes.has(p_from_node))
+ return false;
+
+ if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count())
+ return false;
+
+ if (!g->nodes.has(p_to_node))
+ return false;
+
+ if (p_to_port < 0 || p_to_port >= g->nodes[p_to_node].node->get_input_port_count())
+ return false;
+
+ VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
+ VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
+
+ if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) {
+ return false;
+ }
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT);
+ Graph *g = &graph[p_type];
+
+ ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER);
+
+ VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port);
+ VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port);
+
+ if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) {
+ ERR_EXPLAIN("Incompatible port types (scalar/vec with transform");
+ ERR_FAIL_V(ERR_INVALID_PARAMETER)
+ return ERR_INVALID_PARAMETER;
+ }
+
+ for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ ERR_FAIL_V(ERR_ALREADY_EXISTS);
+ }
+ }
+
+ Connection c;
+ c.from_node = p_from_node;
+ c.from_port = p_from_port;
+ c.to_node = p_to_node;
+ c.to_port = p_to_port;
+ g->connections.push_back(c);
+
+ _queue_update();
+ return OK;
+}
+void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+
+ for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+
+ if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ g->connections.erase(E);
+ _queue_update();
+ return;
+ }
+ }
+}
+
+Array VisualShader::_get_node_connections(Type p_type) const {
+ ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Array());
+ const Graph *g = &graph[p_type];
+
+ Array ret;
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ Dictionary d;
+ d["from_node"] = E->get().from_node;
+ d["from_port"] = E->get().from_port;
+ d["to_node"] = E->get().to_node;
+ d["to_port"] = E->get().to_port;
+ ret.push_back(d);
+ }
+
+ return ret;
+}
+void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ const Graph *g = &graph[p_type];
+
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ r_connections->push_back(E->get());
+ }
+}
+
+void VisualShader::set_mode(Mode p_mode) {
+ if (shader_mode == p_mode) {
+ return;
+ }
+
+ //erase input/output connections
+ modes.clear();
+ flags.clear();
+ shader_mode = p_mode;
+ for (int i = 0; i < TYPE_MAX; i++) {
+
+ for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+
+ Ref<VisualShaderNodeInput> input = E->get().node;
+ if (input.is_valid()) {
+ input->shader_mode = shader_mode;
+ //input->input_index = 0;
+ }
+ }
+
+ Ref<VisualShaderNodeOutput> output = graph[i].nodes[NODE_ID_OUTPUT].node;
+ output->shader_mode = shader_mode;
+
+ // clear connections since they are no longer valid
+ for (List<Connection>::Element *E = graph[i].connections.front(); E;) {
+
+ bool keep = true;
+
+ List<Connection>::Element *N = E->next();
+
+ int from = E->get().from_node;
+ int to = E->get().to_node;
+
+ if (!graph[i].nodes.has(from)) {
+ keep = false;
+ } else {
+ Ref<VisualShaderNode> from_node = graph[i].nodes[from].node;
+ if (from_node->is_class("VisualShaderNodeOutput") || from_node->is_class("VisualShaderNodeInput")) {
+ keep = false;
+ }
+ }
+
+ if (!graph[i].nodes.has(to)) {
+ keep = false;
+ } else {
+ Ref<VisualShaderNode> to_node = graph[i].nodes[to].node;
+ if (to_node->is_class("VisualShaderNodeOutput") || to_node->is_class("VisualShaderNodeInput")) {
+ keep = false;
+ }
+ }
+
+ if (!keep) {
+ graph[i].connections.erase(E);
+ }
+ E = N;
+ }
+ }
+
+ _queue_update();
+ _change_notify();
+}
+
+void VisualShader::set_graph_offset(const Vector2 &p_offset) {
+ graph_offset = p_offset;
+}
+
+Vector2 VisualShader::get_graph_offset() const {
+ return graph_offset;
+}
+
+Shader::Mode VisualShader::get_mode() const {
+ return shader_mode;
+}
+
+String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const {
+
+ Ref<VisualShaderNode> node = get_node(p_type, p_node);
+ ERR_FAIL_COND_V(!node.is_valid(), String());
+ ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String());
+ ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String());
+
+ StringBuilder global_code;
+ StringBuilder code;
+
+ global_code += String() + "shader_type canvas_item;\n";
+
+ //make it faster to go around through shader
+ VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
+ VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
+
+ for (const List<Connection>::Element *E = graph[p_type].connections.front(); E; E = E->next()) {
+ ConnectionKey from_key;
+ from_key.node = E->get().from_node;
+ from_key.port = E->get().from_port;
+
+ output_connections.insert(from_key, E);
+
+ ConnectionKey to_key;
+ to_key.node = E->get().to_node;
+ to_key.port = E->get().to_port;
+
+ input_connections.insert(to_key, E);
+ }
+
+ code += "\nvoid fragment() {\n";
+
+ Set<int> processed;
+ Error err = _write_node(p_type, global_code, code, default_tex_params, input_connections, output_connections, p_node, processed, true);
+ ERR_FAIL_COND_V(err != OK, String());
+
+ if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) {
+ code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
+ } else {
+ code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ }
+ code += "}\n";
+
+ //set code secretly
+ global_code += "\n\n";
+ String final_code = global_code;
+ final_code += code;
+ //print_line(final_code);
+ return final_code;
+}
+
+#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z'))
+
+#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
+
+String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
+
+ String name = p_name; //validate name first
+ while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ name = name.substr(1, name.length() - 1);
+ }
+ if (name != String()) {
+
+ String valid_name;
+
+ for (int i = 0; i < name.length(); i++) {
+ if (IS_SYMBOL_CHAR(name[i])) {
+ valid_name += String::chr(name[i]);
+ } else if (name[i] == ' ') {
+ valid_name += "_";
+ }
+ }
+
+ name = valid_name;
+ }
+
+ if (name == String()) {
+ name = p_uniform->get_caption();
+ }
+
+ int attempt = 1;
+
+ while (true) {
+
+ bool exists = false;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ Ref<VisualShaderNodeUniform> node = E->get().node;
+ if (node == p_uniform) { //do not test on self
+ continue;
+ }
+ if (node.is_valid() && node->get_uniform_name() == name) {
+ exists = true;
+ break;
+ }
+ }
+ if (exists) {
+ break;
+ }
+ }
+
+ if (exists) {
+ //remove numbers, put new and try again
+ attempt++;
+ while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') {
+ name = name.substr(0, name.length() - 1);
+ }
+ ERR_FAIL_COND_V(name == String(), String());
+ name += itos(attempt);
+ } else {
+ break;
+ }
+ }
+
+ return name;
+}
+
+VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = {
+ { Shader::MODE_SPATIAL, "blend" },
+ { Shader::MODE_SPATIAL, "depth_draw" },
+ { Shader::MODE_SPATIAL, "cull" },
+ { Shader::MODE_SPATIAL, "diffuse" },
+ { Shader::MODE_SPATIAL, "specular" },
+ { Shader::MODE_CANVAS_ITEM, "blend" },
+ { Shader::MODE_CANVAS_ITEM, NULL }
+};
+
+static const char *type_string[VisualShader::TYPE_MAX] = {
+ "vertex",
+ "fragment",
+ "light"
+};
+bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
+
+ String name = p_name;
+ if (name == "mode") {
+ set_mode(Shader::Mode(int(p_value)));
+ return true;
+ } else if (name.begins_with("flags/")) {
+ StringName flag = name.get_slicec('/', 1);
+ bool enable = p_value;
+ if (enable) {
+ flags.insert(flag);
+ } else {
+ flags.erase(flag);
+ }
+ _queue_update();
+ return true;
+ } else if (name.begins_with("modes/")) {
+ String mode = name.get_slicec('/', 1);
+ int value = p_value;
+ if (value == 0) {
+ modes.erase(mode); //means its default anyway, so dont store it
+ } else {
+ modes[mode] = value;
+ }
+ _queue_update();
+ return true;
+ } else if (name.begins_with("nodes/")) {
+ String typestr = name.get_slicec('/', 1);
+ Type type = TYPE_VERTEX;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ if (typestr == type_string[i]) {
+ type = Type(i);
+ break;
+ }
+ }
+
+ String index = name.get_slicec('/', 2);
+ if (index == "connections") {
+
+ Vector<int> conns = p_value;
+ if (conns.size() % 4 == 0) {
+ for (int i = 0; i < conns.size(); i += 4) {
+ connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]);
+ }
+ }
+ return true;
+ }
+
+ int id = index.to_int();
+ String what = name.get_slicec('/', 3);
+
+ if (what == "node") {
+ add_node(type, p_value, Vector2(), id);
+ return true;
+ } else if (what == "position") {
+ set_node_position(type, id, p_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String name = p_name;
+ if (name == "mode") {
+ r_ret = get_mode();
+ return true;
+ } else if (name.begins_with("flags/")) {
+ StringName flag = name.get_slicec('/', 1);
+ r_ret = flags.has(flag);
+ return true;
+ } else if (name.begins_with("modes/")) {
+ String mode = name.get_slicec('/', 1);
+ if (modes.has(mode)) {
+ r_ret = modes[mode];
+ } else {
+ r_ret = 0;
+ }
+ return true;
+ } else if (name.begins_with("nodes/")) {
+ String typestr = name.get_slicec('/', 1);
+ Type type = TYPE_VERTEX;
+ for (int i = 0; i < TYPE_MAX; i++) {
+ if (typestr == type_string[i]) {
+ type = Type(i);
+ break;
+ }
+ }
+
+ String index = name.get_slicec('/', 2);
+ if (index == "connections") {
+
+ Vector<int> conns;
+ for (const List<Connection>::Element *E = graph[type].connections.front(); E; E = E->next()) {
+ conns.push_back(E->get().from_node);
+ conns.push_back(E->get().from_port);
+ conns.push_back(E->get().to_node);
+ conns.push_back(E->get().to_port);
+ }
+
+ r_ret = conns;
+ return true;
+ }
+
+ int id = index.to_int();
+ String what = name.get_slicec('/', 3);
+
+ if (what == "node") {
+ r_ret = get_node(type, id);
+ return true;
+ } else if (what == "position") {
+ r_ret = get_node_position(type, id);
+ return true;
+ }
+ }
+ return false;
+}
+void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ //mode
+ p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles"));
+ //render modes
+
+ Map<String, String> blend_mode_enums;
+ Set<String> toggles;
+
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ int idx = 0;
+ bool in_enum = false;
+ while (render_mode_enums[idx].string) {
+ if (mode.begins_with(render_mode_enums[idx].string)) {
+ String begin = render_mode_enums[idx].string;
+ String option = mode.replace_first(begin + "_", "");
+ if (!blend_mode_enums.has(begin)) {
+ blend_mode_enums[begin] = option;
+ } else {
+ blend_mode_enums[begin] += "," + option;
+ }
+ in_enum = true;
+ break;
+ }
+ idx++;
+ }
+
+ if (!in_enum) {
+ toggles.insert(mode);
+ }
+ }
+
+ for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) {
+
+ p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get()));
+ }
+
+ for (Set<String>::Element *E = toggles.front(); E; E = E->next()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get()));
+ }
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+ for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+
+ String prop_name = "nodes/";
+ prop_name += type_string[i];
+ prop_name += "/" + itos(E->key());
+
+ if (E->key() != NODE_ID_OUTPUT) {
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+ p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+}
+
+Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const {
+
+ const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
+
+ //check inputs recursively first
+ int input_count = vsnode->get_input_port_count();
+ for (int i = 0; i < input_count; i++) {
+ ConnectionKey ck;
+ ck.node = node;
+ ck.port = i;
+
+ if (input_connections.has(ck)) {
+ int from_node = input_connections[ck]->get().from_node;
+ if (processed.has(from_node)) {
+ continue;
+ }
+
+ Error err = _write_node(type, global_code, code, def_tex_params, input_connections, output_connections, from_node, processed, for_preview);
+ if (err)
+ return err;
+ }
+ }
+
+ // then this node
+
+ code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ Vector<String> input_vars;
+
+ input_vars.resize(vsnode->get_input_port_count());
+ String *inputs = input_vars.ptrw();
+
+ for (int i = 0; i < input_count; i++) {
+ ConnectionKey ck;
+ ck.node = node;
+ ck.port = i;
+
+ if (input_connections.has(ck)) {
+ //connected to something, use that output
+ int from_node = input_connections[ck]->get().from_node;
+ int from_port = input_connections[ck]->get().from_port;
+
+ VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i);
+ VisualShaderNode::PortType out_type = graph[type].nodes[from_node].node->get_output_port_type(from_port);
+
+ String src_var = "n_out" + itos(from_node) + "p" + itos(from_port);
+
+ if (in_type == out_type) {
+ inputs[i] = src_var;
+ } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
+ inputs[i] = "dot(" + src_var + ",vec3(0.333333,0.333333,0.333333))";
+ } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
+ inputs[i] = "vec3(" + src_var + ")";
+ }
+ } else {
+
+ Variant defval = vsnode->get_input_port_default_value(i);
+ if (defval.get_type() == Variant::REAL || defval.get_type() == Variant::INT) {
+ float val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
+ } else if (defval.get_type() == Variant::VECTOR3) {
+ Vector3 val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f,%.5f,%.5f);\n", val.x, val.y, val.z);
+ } else if (defval.get_type() == Variant::TRANSFORM) {
+ Transform val = defval;
+ val.basis.transpose();
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ Array values;
+ for (int i = 0; i < 3; i++) {
+ values.push_back(val.basis[i].x);
+ values.push_back(val.basis[i].y);
+ values.push_back(val.basis[i].z);
+ }
+ values.push_back(val.origin.x);
+ values.push_back(val.origin.y);
+ values.push_back(val.origin.z);
+ bool err = false;
+ code += "\tmat4 " + inputs[i] + " = " + String("mat4( vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,1.0) );\n").sprintf(values, &err);
+ } else {
+ //will go empty, node is expected to know what it is doing at this point and handle it
+ }
+ }
+ }
+
+ int output_count = vsnode->get_output_port_count();
+ Vector<String> output_vars;
+ output_vars.resize(vsnode->get_output_port_count());
+ String *outputs = output_vars.ptrw();
+
+ for (int i = 0; i < output_count; i++) {
+
+ outputs[i] = "n_out" + itos(node) + "p" + itos(i);
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: code += String() + "\tfloat " + outputs[i] + ";\n"; break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break;
+ case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break;
+ default: {}
+ }
+ }
+
+ Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node);
+ for (int i = 0; i < params.size(); i++) {
+ def_tex_params.push_back(params[i]);
+ }
+
+ Ref<VisualShaderNodeInput> input = vsnode;
+
+ if (input.is_valid() && for_preview) {
+ //handle for preview
+ code += input->generate_code_for_preview(type, node, inputs, outputs);
+ } else {
+ //handle normally
+ global_code += vsnode->generate_global(get_mode(), type, node);
+ code += vsnode->generate_code(get_mode(), type, node, inputs, outputs);
+ }
+ code += "\n"; //
+ processed.insert(node);
+
+ return OK;
+}
+
+void VisualShader::_update_shader() const {
+ if (!dirty)
+ return;
+
+ dirty = false;
+
+ StringBuilder global_code;
+ StringBuilder code;
+ Vector<VisualShader::DefaultTextureParam> default_tex_params;
+ static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles" };
+
+ global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n";
+
+ String render_mode;
+
+ {
+ //fill render mode enums
+ int idx = 0;
+ while (render_mode_enums[idx].string) {
+
+ if (shader_mode == render_mode_enums[idx].mode) {
+
+ if (modes.has(render_mode_enums[idx].string)) {
+
+ int which = modes[render_mode_enums[idx].string];
+ int count = 0;
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ if (mode.begins_with(render_mode_enums[idx].string)) {
+ if (count == which) {
+ if (render_mode != String()) {
+ render_mode += ", ";
+ }
+ render_mode += mode;
+ break;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ idx++;
+ }
+
+ //fill render mode flags
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) {
+
+ String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i];
+ if (flags.has(mode)) {
+ if (render_mode != String()) {
+ render_mode += ", ";
+ }
+ render_mode += mode;
+ }
+ }
+ }
+
+ if (render_mode != String()) {
+
+ global_code += "render_mode " + render_mode + ";\n\n";
+ }
+
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" };
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+
+ //make it faster to go around through shader
+ VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
+ VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
+
+ for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) {
+ ConnectionKey from_key;
+ from_key.node = E->get().from_node;
+ from_key.port = E->get().from_port;
+
+ output_connections.insert(from_key, E);
+
+ ConnectionKey to_key;
+ to_key.node = E->get().to_node;
+ to_key.port = E->get().to_port;
+
+ input_connections.insert(to_key, E);
+ }
+
+ code += "\nvoid " + String(func_name[i]) + "() {\n";
+
+ Set<int> processed;
+ Error err = _write_node(Type(i), global_code, code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false);
+ ERR_FAIL_COND(err != OK);
+
+ code += "}\n";
+ }
+
+ //set code secretly
+ global_code += "\n\n";
+ String final_code = global_code;
+ final_code += code;
+ const_cast<VisualShader *>(this)->set_code(final_code);
+ //print_line(final_code);
+ for (int i = 0; i < default_tex_params.size(); i++) {
+ const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param);
+ }
+}
+
+void VisualShader::_queue_update() {
+ if (dirty) {
+ return;
+ }
+
+ dirty = true;
+ call_deferred("_update_shader");
+}
+
+void VisualShader::_input_type_changed(Type p_type, int p_id) {
+ //erase connections using this input, as type changed
+ Graph *g = &graph[p_type];
+
+ for (List<Connection>::Element *E = g->connections.front(); E;) {
+ List<Connection>::Element *N = E->next();
+ if (E->get().from_node == p_id) {
+ g->connections.erase(E);
+ }
+ E = N;
+ }
+}
+
+void VisualShader::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode);
+
+ ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node);
+ ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position);
+
+ ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position);
+ ClassDB::bind_method(D_METHOD("get_node", "type"), &VisualShader::get_node);
+
+ ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list);
+ ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id);
+
+ ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node);
+
+ ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
+ ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
+
+ ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes);
+ ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes);
+
+ ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections);
+
+ ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset);
+ ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset);
+
+ ClassDB::bind_method(D_METHOD("_queue_update"), &VisualShader::_queue_update);
+ ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
+
+ ClassDB::bind_method(D_METHOD("_input_type_changed"), &VisualShader::_input_type_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
+
+ BIND_ENUM_CONSTANT(TYPE_VERTEX);
+ BIND_ENUM_CONSTANT(TYPE_FRAGMENT);
+ BIND_ENUM_CONSTANT(TYPE_LIGHT);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
+
+ BIND_CONSTANT(NODE_ID_INVALID);
+ BIND_CONSTANT(NODE_ID_OUTPUT);
+}
+
+VisualShader::VisualShader() {
+ shader_mode = Shader::MODE_SPATIAL;
+
+ for (int i = 0; i < TYPE_MAX; i++) {
+ Ref<VisualShaderNodeOutput> output;
+ output.instance();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
+ }
+
+ dirty = true;
+}
+
+///////////////////////////////////////////////////////////
+
+const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
+ // Spatial, Vertex
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0)" },
+
+ // Spatial, Fragment
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_alpha", "LIGHT_COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "index", "float(INDEX)" },
+
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
+
+ // Spatial, Fragment
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0,1.0,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0,0.0,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0,1.0, 0.0)" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0, 1.0, 0.0)" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0,0.0,1.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+int VisualShaderNodeInput::get_input_port_count() const {
+
+ return 0;
+}
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_port_type(int p_port) const {
+
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeInput::get_input_port_name(int p_port) const {
+
+ return "";
+}
+
+int VisualShaderNodeInput::get_output_port_count() const {
+
+ return 1;
+}
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const {
+
+ return get_input_type_by_name(input_name);
+}
+String VisualShaderNodeInput::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeInput::get_caption() const {
+ return TTR("Input");
+}
+
+String VisualShaderNodeInput::generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+
+ String code;
+
+ while (preview_ports[idx].mode != Shader::MODE_MAX) {
+ if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) {
+ code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n";
+ break;
+ }
+ idx++;
+ }
+
+ if (code == String()) {
+ switch (get_output_port_type(0)) {
+ case PORT_TYPE_SCALAR: {
+ code = "\t" + p_output_vars[0] + " = 0.0;\n";
+ } break; //default (none found) is scalar
+ case PORT_TYPE_VECTOR: {
+ code = "\t" + p_output_vars[0] + " = vec3(0.0);\n";
+ } break; //default (none found) is scalar
+ case PORT_TYPE_TRANSFORM: {
+ code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n";
+ } break; //default (none found) is scalar
+ }
+ }
+
+ return code;
+}
+
+String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+
+ String code;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) {
+ code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n";
+ break;
+ }
+ idx++;
+ }
+
+ if (code == String()) {
+ code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar
+ }
+
+ return code;
+}
+
+void VisualShaderNodeInput::set_input_name(String p_name) {
+ PortType prev_type = get_input_type_by_name(input_name);
+ input_name = p_name;
+ emit_changed();
+ if (get_input_type_by_name(input_name) != prev_type) {
+ emit_signal("input_type_changed");
+ }
+}
+
+String VisualShaderNodeInput::get_input_name() const {
+ return input_name;
+}
+
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_type_by_name(String p_name) const {
+
+ int idx = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == p_name) {
+ return ports[idx].type;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+int VisualShaderNodeInput::get_input_index_count() const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ count++;
+ }
+ idx++;
+ }
+
+ return count;
+}
+
+VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_index_type(int p_index) const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_index) {
+ return ports[idx].type;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeInput::get_input_index_name(int p_index) const {
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_index) {
+ return ports[idx].name;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return "";
+}
+
+void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const {
+
+ if (property.name == "input_name") {
+ String port_list;
+
+ int idx = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (port_list != String()) {
+ port_list += ",";
+ }
+ port_list += ports[idx].name;
+ }
+ idx++;
+ }
+
+ if (port_list == "") {
+ port_list = TTR("None");
+ }
+ property.hint_string = port_list;
+ }
+}
+
+Vector<StringName> VisualShaderNodeInput::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("input_name");
+ return props;
+}
+
+void VisualShaderNodeInput::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_name", PROPERTY_HINT_ENUM, ""), "set_input_name", "get_input_name");
+ ADD_SIGNAL(MethodInfo("input_type_changed"));
+}
+VisualShaderNodeInput::VisualShaderNodeInput() {
+ input_name = "[None]";
+ // changed when set
+ shader_type = VisualShader::TYPE_MAX;
+ shader_mode = Shader::MODE_MAX;
+}
+
+////////////////////////////////////////////
+
+const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
+ // Spatial, Vertex
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ // Spatial, Fragment
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
+
+ // Spatial, Light
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
+ // Canvas Item, Vertex
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
+ // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.rgb" },
+ // Particles, Vertex
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL },
+};
+
+int VisualShaderNodeOutput::get_input_port_count() const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ count++;
+ }
+ idx++;
+ }
+
+ return count;
+}
+
+VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_input_port_type(int p_port) const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_port) {
+ return ports[idx].type;
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeOutput::get_input_port_name(int p_port) const {
+
+ int idx = 0;
+ int count = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+ if (count == p_port) {
+ return String(ports[idx].name).capitalize();
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return String();
+}
+
+Variant VisualShaderNodeOutput::get_input_port_default_value(int p_port) const {
+ return Variant();
+}
+
+int VisualShaderNodeOutput::get_output_port_count() const {
+
+ return 0;
+}
+VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeOutput::get_output_port_name(int p_port) const {
+ return String();
+}
+
+bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
+
+ if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
+ String name = get_input_port_name(p_index);
+ return (name == "Normal" || name == "Rim" || name == "Alpha Scissor");
+ }
+ return false;
+}
+
+String VisualShaderNodeOutput::get_caption() const {
+ return TTR("Output");
+}
+
+String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ int idx = 0;
+ int count = 0;
+
+ String code;
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
+
+ if (p_input_vars[count] != String()) {
+ String s = ports[idx].string;
+ if (s.find(":") != -1) {
+ code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
+ } else {
+ code += "\t" + s + " = " + p_input_vars[count] + ";\n";
+ }
+ }
+ count++;
+ }
+ idx++;
+ }
+
+ return code;
+}
+
+VisualShaderNodeOutput::VisualShaderNodeOutput() {
+}
+
+///////////////////////////
+
+void VisualShaderNodeUniform::set_uniform_name(const String &p_name) {
+ uniform_name = p_name;
+ emit_signal("name_changed");
+ emit_changed();
+}
+
+String VisualShaderNodeUniform::get_uniform_name() const {
+ return uniform_name;
+}
+
+void VisualShaderNodeUniform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniform::set_uniform_name);
+ ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniform::get_uniform_name);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "uniform_name"), "set_uniform_name", "get_uniform_name");
+}
+
+VisualShaderNodeUniform::VisualShaderNodeUniform() {
+}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
new file mode 100644
index 0000000000..6ff1c9a9fe
--- /dev/null
+++ b/scene/resources/visual_shader.h
@@ -0,0 +1,284 @@
+#ifndef VISUAL_SHADER_H
+#define VISUAL_SHADER_H
+
+#include "scene/resources/shader.h"
+#include "string_builder.h"
+
+class VisualShaderNodeUniform;
+class VisualShaderNode;
+
+class VisualShader : public Shader {
+ GDCLASS(VisualShader, Shader)
+public:
+ enum Type {
+ TYPE_VERTEX,
+ TYPE_FRAGMENT,
+ TYPE_LIGHT,
+ TYPE_MAX
+ };
+
+ struct Connection {
+ int from_node;
+ int from_port;
+ int to_node;
+ int to_port;
+ };
+
+ struct DefaultTextureParam {
+ StringName name;
+ Ref<Texture> param;
+ };
+
+private:
+ struct Node {
+ Ref<VisualShaderNode> node;
+ Vector2 position;
+ };
+
+ struct Graph {
+ Map<int, Node> nodes;
+ List<Connection> connections;
+ } graph[TYPE_MAX];
+
+ Shader::Mode shader_mode;
+
+ Array _get_node_connections(Type p_type) const;
+
+ Vector2 graph_offset;
+
+ struct RenderModeEnums {
+ Shader::Mode mode;
+ const char *string;
+ };
+
+ HashMap<String, int> modes;
+ Set<StringName> flags;
+
+ static RenderModeEnums render_mode_enums[];
+
+ volatile mutable bool dirty;
+ void _queue_update();
+
+ union ConnectionKey {
+
+ struct {
+ uint64_t node : 32;
+ uint64_t port : 32;
+ };
+ uint64_t key;
+ bool operator<(const ConnectionKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const;
+
+ void _input_type_changed(Type p_type, int p_id);
+
+protected:
+ virtual void _update_shader() const;
+ static void _bind_methods();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ enum {
+ NODE_ID_INVALID = -1,
+ NODE_ID_OUTPUT = 0,
+ };
+
+ void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id);
+ void set_node_position(Type p_type, int p_id, const Vector2 &p_position);
+
+ Vector2 get_node_position(Type p_type, int p_id) const;
+ Ref<VisualShaderNode> get_node(Type p_type, int p_id) const;
+
+ Vector<int> get_node_list(Type p_type) const;
+ int get_valid_node_id(Type p_type) const;
+
+ int find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const;
+ void remove_node(Type p_type, int p_id);
+
+ bool is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
+ bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
+ Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+
+ void get_node_connections(Type p_type, List<Connection> *r_connections) const;
+
+ void set_mode(Mode p_mode);
+ virtual Mode get_mode() const;
+
+ void set_graph_offset(const Vector2 &p_offset);
+ Vector2 get_graph_offset() const;
+
+ String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
+
+ String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
+
+ VisualShader();
+};
+
+VARIANT_ENUM_CAST(VisualShader::Type)
+///
+///
+///
+
+class VisualShaderNode : public Resource {
+ GDCLASS(VisualShaderNode, Resource)
+
+ int port_preview;
+
+ Map<int, Variant> default_input_values;
+
+ Array _get_default_input_values() const;
+ void _set_default_input_values(const Array &p_values);
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum PortType {
+ PORT_TYPE_SCALAR,
+ PORT_TYPE_VECTOR,
+ PORT_TYPE_TRANSFORM,
+ };
+
+ virtual String get_caption() const = 0;
+
+ virtual int get_input_port_count() const = 0;
+ virtual PortType get_input_port_type(int p_port) const = 0;
+ virtual String get_input_port_name(int p_port) const = 0;
+
+ void set_input_port_default_value(int p_port, const Variant &p_value);
+ Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
+
+ virtual int get_output_port_count() const = 0;
+ virtual PortType get_output_port_type(int p_port) const = 0;
+ virtual String get_output_port_name(int p_port) const = 0;
+
+ void set_output_port_for_preview(int p_index);
+ int get_output_port_for_preview() const;
+
+ virtual bool is_port_separator(int p_index) const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const = 0; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const;
+
+ VisualShaderNode();
+};
+/////
+
+class VisualShaderNodeInput : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeInput, VisualShaderNode)
+
+ friend class VisualShader;
+ VisualShader::Type shader_type;
+ Shader::Mode shader_mode;
+
+ struct Port {
+ Shader::Mode mode;
+ VisualShader::Type shader_type;
+ PortType type;
+ const char *name;
+ const char *string;
+ };
+
+ static const Port ports[];
+ static const Port preview_ports[];
+
+ String input_name;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String get_caption() const;
+
+ virtual String generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const;
+
+ void set_input_name(String p_name);
+ String get_input_name() const;
+
+ int get_input_index_count() const;
+ PortType get_input_index_type(int p_index) const;
+ String get_input_index_name(int p_index) const;
+
+ PortType get_input_type_by_name(String p_name) const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeInput();
+};
+
+///
+
+class VisualShaderNodeOutput : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeOutput, VisualShaderNode)
+public:
+ friend class VisualShader;
+ VisualShader::Type shader_type;
+ Shader::Mode shader_mode;
+
+ struct Port {
+ Shader::Mode mode;
+ VisualShader::Type shader_type;
+ PortType type;
+ const char *name;
+ const char *string;
+ };
+
+ static const Port ports[];
+
+public:
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+ Variant get_input_port_default_value(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual bool is_port_separator(int p_index) const;
+
+ virtual String get_caption() const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const;
+
+ VisualShaderNodeOutput();
+};
+
+class VisualShaderNodeUniform : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeUniform, VisualShaderNode)
+
+ String uniform_name;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_uniform_name(const String &p_name);
+ String get_uniform_name() const;
+
+ VisualShaderNodeUniform();
+};
+
+#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
new file mode 100644
index 0000000000..98ecdbdf30
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -0,0 +1,1896 @@
+#include "visual_shader_nodes.h"
+////////////// Scalar
+
+String VisualShaderNodeScalarConstant::get_caption() const {
+ return "Scalar";
+}
+
+int VisualShaderNodeScalarConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeScalarConstant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarConstant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
+}
+
+void VisualShaderNodeScalarConstant::set_constant(float p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+float VisualShaderNodeScalarConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeScalarConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeScalarConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeScalarConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeScalarConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() {
+ constant = 0;
+}
+
+////////////// Color
+
+String VisualShaderNodeColorConstant::get_caption() const {
+ return "Color";
+}
+
+int VisualShaderNodeColorConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeColorConstant::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.r, constant.g, constant.b) + ";\n";
+ code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n";
+
+ return code;
+}
+
+void VisualShaderNodeColorConstant::set_constant(Color p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Color VisualShaderNodeColorConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeColorConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
+ constant = Color(1, 1, 1, 1);
+}
+
+////////////// Vector
+
+String VisualShaderNodeVec3Constant::get_caption() const {
+ return "Vector";
+}
+
+int VisualShaderNodeVec3Constant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec3Constant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.x, constant.y, constant.z) + ";\n";
+}
+
+void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Vector3 VisualShaderNodeVec3Constant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeVec3Constant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() {
+}
+
+////////////// Transform
+
+String VisualShaderNodeTransformConstant::get_caption() const {
+ return "Transform";
+}
+
+int VisualShaderNodeTransformConstant::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeTransformConstant::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ Transform t = constant;
+ t.basis.transpose();
+
+ String code = "\t" + p_output_vars[0] + " = mat4(";
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[0].x, t.basis[0].y, t.basis[0].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[1].x, t.basis[1].y, t.basis[1].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[2].x, t.basis[2].y, t.basis[2].z);
+ code += vformat("vec4(%.6f,%.6f,%.6f,1.0) );\n", t.origin.x, t.origin.y, t.origin.z);
+ return code;
+}
+
+void VisualShaderNodeTransformConstant::set_constant(Transform p_value) {
+
+ constant = p_value;
+ emit_changed();
+}
+
+Transform VisualShaderNodeTransformConstant::get_constant() const {
+
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeTransformConstant::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() {
+}
+
+////////////// Texture
+
+String VisualShaderNodeTexture::get_caption() const {
+ return "Texture";
+}
+
+int VisualShaderNodeTexture::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTexture::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeTexture::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
+
+ static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" };
+ return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "tex");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ if (source == SOURCE_TEXTURE) {
+
+ String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "tex");
+ switch (texture_type) {
+ case TYPE_DATA: break;
+ case TYPE_COLOR: u += " : hint_color"; break;
+ case TYPE_NORMALMAP: u += " : hint_normal"; break;
+ }
+ return u + ";";
+ }
+
+ return String();
+}
+
+String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ if (source == SOURCE_TEXTURE) {
+ String id = make_unique_id(p_type, p_id, "tex");
+ String code;
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\tvec4 " + id + "_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
+ return code;
+ }
+
+ if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = texture( TEXTURE , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 _tex_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+ }
+
+ //none
+ String code;
+ code += "\t" + p_output_vars[0] + " = vec3(0.0);\n";
+ code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ return code;
+}
+
+void VisualShaderNodeTexture::set_source(Source p_source) {
+ source = p_source;
+ emit_changed();
+ emit_signal("editor_refresh_request");
+}
+
+VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const {
+ return source;
+}
+
+void VisualShaderNodeTexture::set_texture(Ref<Texture> p_value) {
+
+ texture = p_value;
+ emit_changed();
+}
+
+Ref<Texture> VisualShaderNodeTexture::get_texture() const {
+
+ return texture;
+}
+
+void VisualShaderNodeTexture::set_texture_type(TextureType p_type) {
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeTexture::TextureType VisualShaderNodeTexture::get_texture_type() const {
+ return texture_type;
+}
+
+Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("source");
+ if (source == SOURCE_TEXTURE) {
+ props.push_back("texture");
+ props.push_back("texture_type");
+ }
+ return props;
+}
+
+String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+
+ if (source == SOURCE_TEXTURE) {
+ return String(); // all good
+ }
+
+ if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ return String(); // all good
+ }
+
+ if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+
+ return String(); // all good
+ }
+
+ if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM) {
+
+ return String(); // all good
+ }
+
+ return TTR("Invalid source for shader.");
+}
+
+void VisualShaderNodeTexture::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeTexture::set_source);
+ ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeTexture::get_source);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "value"), &VisualShaderNodeTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeTexture::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D"), "set_source", "get_source");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+
+ BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
+ BIND_ENUM_CONSTANT(SOURCE_SCREEN);
+ BIND_ENUM_CONSTANT(SOURCE_2D_TEXTURE);
+ BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL);
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+}
+
+VisualShaderNodeTexture::VisualShaderNodeTexture() {
+ texture_type = TYPE_DATA;
+ source = SOURCE_TEXTURE;
+}
+
+////////////// CubeMap
+
+String VisualShaderNodeCubeMap::get_caption() const {
+ return "CubeMap";
+}
+
+int VisualShaderNodeCubeMap::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeCubeMap::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMap::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "cube");
+ dtp.param = cube_map;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube");
+ switch (texture_type) {
+ case TYPE_DATA: break;
+ case TYPE_COLOR: u += " : hint_color"; break;
+ case TYPE_NORMALMAP: u += " : hint_normal"; break;
+ }
+ return u + ";";
+}
+
+String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String id = make_unique_id(p_type, p_id, "cube");
+ String code;
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\tvec4 " + id + "_read = vec4(0.0);\n";
+
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + " );\n";
+ } else {
+ code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
+ return code;
+}
+
+void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) {
+
+ cube_map = p_value;
+ emit_changed();
+}
+
+Ref<CubeMap> VisualShaderNodeCubeMap::get_cube_map() const {
+
+ return cube_map;
+}
+
+void VisualShaderNodeCubeMap::set_texture_type(TextureType p_type) {
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeCubeMap::TextureType VisualShaderNodeCubeMap::get_texture_type() const {
+ return texture_type;
+}
+
+Vector<StringName> VisualShaderNodeCubeMap::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("cube_map");
+ props.push_back("texture_type");
+ return props;
+}
+
+void VisualShaderNodeCubeMap::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubeMap::set_cube_map);
+ ClassDB::bind_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubeMap::get_cube_map);
+
+ ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeCubeMap::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubeMap::get_texture_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "CubeMap"), "set_cube_map", "get_cube_map");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+}
+
+VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() {
+ texture_type = TYPE_DATA;
+}
+////////////// Scalar Op
+
+String VisualShaderNodeScalarOp::get_caption() const {
+ return "ScalarOp";
+}
+
+int VisualShaderNodeScalarOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeScalarOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code = "\t" + p_output_vars[0] + " = ";
+ switch (op) {
+
+ case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break;
+ case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break;
+ case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break;
+ case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break;
+ case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeScalarOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeScalarOp::Operator VisualShaderNodeScalarOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeScalarOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeScalarOp::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeScalarOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeScalarOp::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_SUB);
+ BIND_ENUM_CONSTANT(OP_MUL);
+ BIND_ENUM_CONSTANT(OP_DIV);
+ BIND_ENUM_CONSTANT(OP_MOD);
+ BIND_ENUM_CONSTANT(OP_POW);
+ BIND_ENUM_CONSTANT(OP_MAX);
+ BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_ATAN2);
+}
+
+VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() {
+ op = OP_ADD;
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+}
+
+////////////// Vector Op
+
+String VisualShaderNodeVectorOp::get_caption() const {
+ return "VectorOp";
+}
+
+int VisualShaderNodeVectorOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeVectorOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code = "\t" + p_output_vars[0] + " = ";
+ switch (op) {
+
+ case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break;
+ case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break;
+ case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break;
+ case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break;
+ case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ case OP_CROSS: code += "cross( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeVectorOp::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_SUB);
+ BIND_ENUM_CONSTANT(OP_MUL);
+ BIND_ENUM_CONSTANT(OP_DIV);
+ BIND_ENUM_CONSTANT(OP_MOD);
+ BIND_ENUM_CONSTANT(OP_POW);
+ BIND_ENUM_CONSTANT(OP_MAX);
+ BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_CROSS);
+}
+
+VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
+ op = OP_ADD;
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Color Op
+
+String VisualShaderNodeColorOp::get_caption() const {
+ return "ColorOp";
+}
+
+int VisualShaderNodeColorOp::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorOp::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeColorOp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorOp::get_output_port_name(int p_port) const {
+ return "op"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String code;
+ static const char *axisn[3] = { "x", "y", "z" };
+ switch (op) {
+ case OP_SCREEN: {
+
+ code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")*(vec3(1.0)-" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_DIFFERENCE: {
+
+ code += "\t" + p_output_vars[0] + "=abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_DARKEN: {
+
+ code += "\t" + p_output_vars[0] + "=min(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n";
+ } break;
+ case OP_LIGHTEN: {
+
+ code += "\t" + p_output_vars[0] + "=max(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n";
+
+ } break;
+ case OP_OVERLAY: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ case OP_DODGE: {
+
+ code += "\t" + p_output_vars[0] + "=(" + p_input_vars[0] + ")/(vec3(1.0)-" + p_input_vars[1] + ");\n";
+
+ } break;
+ case OP_BURN: {
+
+ code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")/(" + p_input_vars[1] + ");\n";
+ } break;
+ case OP_SOFT_LIGHT: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend+0.5));\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-(blend-0.5)));\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ case OP_HARD_LIGHT: {
+
+ for (int i = 0; i < 3; i++) {
+ code += "\t{\n";
+ code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += "\t\tif (base < 0.5) {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0*blend));\n";
+ code += "\t\t} else {\n";
+ code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-2.0*(blend-0.5)));\n";
+ code += "\t\t}\n";
+ code += "\t}\n";
+ }
+
+ } break;
+ }
+
+ return code;
+}
+
+void VisualShaderNodeColorOp::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeColorOp::Operator VisualShaderNodeColorOp::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeColorOp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeColorOp::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_SCREEN);
+ BIND_ENUM_CONSTANT(OP_DIFFERENCE);
+ BIND_ENUM_CONSTANT(OP_DARKEN);
+ BIND_ENUM_CONSTANT(OP_LIGHTEN);
+ BIND_ENUM_CONSTANT(OP_OVERLAY);
+ BIND_ENUM_CONSTANT(OP_DODGE);
+ BIND_ENUM_CONSTANT(OP_BURN);
+ BIND_ENUM_CONSTANT(OP_SOFT_LIGHT);
+ BIND_ENUM_CONSTANT(OP_HARD_LIGHT);
+}
+
+VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
+ op = OP_SCREEN;
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Transform Mult
+
+String VisualShaderNodeTransformMult::get_caption() const {
+ return "TransformMult";
+}
+
+int VisualShaderNodeTransformMult::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeTransformMult::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const {
+ return "mult"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ if (op == OP_AxB) {
+ return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
+ } else {
+ return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
+ }
+}
+
+void VisualShaderNodeTransformMult::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeTransformMult::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_AxB);
+ BIND_ENUM_CONSTANT(OP_BxA);
+}
+
+VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
+ op = OP_AxB;
+ set_input_port_default_value(0, Transform());
+ set_input_port_default_value(1, Transform());
+}
+
+////////////// TransformVec Mult
+
+String VisualShaderNodeTransformVecMult::get_caption() const {
+ return "TransformVectorMult";
+}
+
+int VisualShaderNodeTransformVecMult::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeTransformVecMult::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ if (op == OP_AxB) {
+ return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0) ).xyz;\n";
+ } else if (op == OP_BxA) {
+ return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + " ).xyz;\n";
+ } else if (op == OP_3x3_AxB) {
+ return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0) ).xyz;\n";
+ } else {
+ return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + " ).xyz;\n";
+ }
+}
+
+void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) {
+
+ op = p_op;
+ emit_changed();
+}
+
+VisualShaderNodeTransformVecMult::Operator VisualShaderNodeTransformVecMult::get_operator() const {
+
+ return op;
+}
+
+Vector<StringName> VisualShaderNodeTransformVecMult::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("operator");
+ return props;
+}
+
+void VisualShaderNodeTransformVecMult::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformVecMult::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformVecMult::get_operator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B (3x3),B x A (3x3)"), "set_operator", "get_operator");
+
+ BIND_ENUM_CONSTANT(OP_AxB);
+ BIND_ENUM_CONSTANT(OP_BxA);
+ BIND_ENUM_CONSTANT(OP_3x3_AxB);
+ BIND_ENUM_CONSTANT(OP_3x3_BxA);
+}
+
+VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
+ op = OP_AxB;
+ set_input_port_default_value(0, Transform());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Scalar Func
+
+String VisualShaderNodeScalarFunc::get_caption() const {
+ return "ScalarFunc";
+}
+
+int VisualShaderNodeScalarFunc::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeScalarFunc::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ static const char *scalar_func_id[FUNC_NEGATE + 1] = {
+ "sin($)",
+ "cos($)",
+ "tan($)",
+ "asin($)",
+ "acos($)",
+ "atan($)",
+ "sinh($)",
+ "cosh($)",
+ "tanh($)",
+ "log($)",
+ "exp($)",
+ "sqrt($)",
+ "abs($)",
+ "sign($)",
+ "floor($)",
+ "round($)",
+ "ceil($)",
+ "fract($)",
+ "min(max($,0),1)",
+ "-($)",
+ };
+
+ return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+}
+
+void VisualShaderNodeScalarFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeScalarFunc::Function VisualShaderNodeScalarFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeScalarFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeScalarFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_SIN);
+ BIND_ENUM_CONSTANT(FUNC_COS);
+ BIND_ENUM_CONSTANT(FUNC_TAN);
+ BIND_ENUM_CONSTANT(FUNC_ASIN);
+ BIND_ENUM_CONSTANT(FUNC_ACOS);
+ BIND_ENUM_CONSTANT(FUNC_ATAN);
+ BIND_ENUM_CONSTANT(FUNC_SINH);
+ BIND_ENUM_CONSTANT(FUNC_COSH);
+ BIND_ENUM_CONSTANT(FUNC_TANH);
+ BIND_ENUM_CONSTANT(FUNC_LOG);
+ BIND_ENUM_CONSTANT(FUNC_EXP);
+ BIND_ENUM_CONSTANT(FUNC_SQRT);
+ BIND_ENUM_CONSTANT(FUNC_ABS);
+ BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_FLOOR);
+ BIND_ENUM_CONSTANT(FUNC_ROUND);
+ BIND_ENUM_CONSTANT(FUNC_CEIL);
+ BIND_ENUM_CONSTANT(FUNC_FRAC);
+ BIND_ENUM_CONSTANT(FUNC_SATURATE);
+ BIND_ENUM_CONSTANT(FUNC_NEGATE);
+}
+
+VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() {
+ func = FUNC_SIGN;
+ set_input_port_default_value(0, 0.0);
+}
+
+////////////// Vector Func
+
+String VisualShaderNodeVectorFunc::get_caption() const {
+ return "VectorFunc";
+}
+
+int VisualShaderNodeVectorFunc::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVectorFunc::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ static const char *vec_func_id[FUNC_HSV2RGB + 1] = {
+ "normalize($)",
+ "max(min($,vec3(1.0)),vec3(0.0))",
+ "-($)",
+ "1.0/($)",
+ "",
+ "",
+ };
+
+ String code;
+
+ if (func == FUNC_RGB2HSV) {
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
+ code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
+ code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
+ code += "\t\tfloat d = q.x - min(q.w, q.y);\n";
+ code += "\t\tfloat e = 1.0e-10;\n";
+ code += "\t\t" + p_output_vars[0] + "=vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
+ code += "\t}\n";
+ } else if (func == FUNC_HSV2RGB) {
+ code += "\t{\n";
+ code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
+ code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
+ code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
+ code += "\t\t" + p_output_vars[0] + "=c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
+ code += "\t}\n";
+
+ } else {
+ code += "\t" + p_output_vars[0] + "=" + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ }
+
+ return code;
+}
+
+void VisualShaderNodeVectorFunc::set_function(Function p_func) {
+
+ func = p_func;
+ emit_changed();
+}
+
+VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function() const {
+
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeVectorFunc::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_NORMALIZE);
+ BIND_ENUM_CONSTANT(FUNC_SATURATE);
+ BIND_ENUM_CONSTANT(FUNC_NEGATE);
+ BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
+ BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
+ BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
+}
+
+VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
+ func = FUNC_NORMALIZE;
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Dot Product
+
+String VisualShaderNodeDotProduct::get_caption() const {
+ return "DotProduct";
+}
+
+int VisualShaderNodeDotProduct::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "a" : "b";
+}
+
+int VisualShaderNodeDotProduct::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const {
+ return "dot";
+}
+
+String VisualShaderNodeDotProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = dot( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n";
+}
+
+VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+}
+
+////////////// Vector Len
+
+String VisualShaderNodeVectorLen::get_caption() const {
+ return "VectorLen";
+}
+
+int VisualShaderNodeVectorLen::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVectorLen::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const {
+ return "length";
+}
+
+String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = length( " + p_input_vars[0] + " );\n";
+}
+
+VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Scalar Interp
+
+String VisualShaderNodeScalarInterp::get_caption() const {
+ return "ScalarInterp";
+}
+
+int VisualShaderNodeScalarInterp::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "a";
+ } else if (p_port == 1) {
+ return "b";
+ } else {
+ return "c";
+ }
+}
+
+int VisualShaderNodeScalarInterp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarInterp::get_output_port_name(int p_port) const {
+ return "mix";
+}
+
+String VisualShaderNodeScalarInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+////////////// Vector Interp
+
+String VisualShaderNodeVectorInterp::get_caption() const {
+ return "VectorInterp";
+}
+
+int VisualShaderNodeVectorInterp::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorInterp::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "a";
+ } else if (p_port == 1) {
+ return "b";
+ } else {
+ return "c";
+ }
+}
+
+int VisualShaderNodeVectorInterp::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorInterp::get_output_port_name(int p_port) const {
+ return "mix";
+}
+
+String VisualShaderNodeVectorInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorInterp::VisualShaderNodeVectorInterp() {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ set_input_port_default_value(2, Vector3());
+}
+
+////////////// Vector Compose
+String VisualShaderNodeVectorCompose::get_caption() const {
+ return "VectorCompose";
+}
+
+int VisualShaderNodeVectorCompose::get_input_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else {
+ return "z";
+ }
+}
+
+int VisualShaderNodeVectorCompose::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const {
+ return "vec";
+}
+
+String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = vec3( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n";
+}
+
+VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() {
+
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+////////////// Transform Compose
+
+String VisualShaderNodeTransformCompose::get_caption() const {
+ return "TransformCompose";
+}
+
+int VisualShaderNodeTransformCompose::get_input_port_count() const {
+ return 4;
+}
+VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else if (p_port == 2) {
+ return "z";
+ } else {
+ return "origin";
+ }
+}
+
+int VisualShaderNodeTransformCompose::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const {
+ return "xform";
+}
+
+String VisualShaderNodeTransformCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = mat4( vec4(" + p_input_vars[0] + ", 0.0) , vec4(" + p_input_vars[1] + ", 0.0) , vec4(" + p_input_vars[2] + ",0.0), vec4(" + p_input_vars[3] + ",1.0) );\n";
+}
+
+VisualShaderNodeTransformCompose::VisualShaderNodeTransformCompose() {
+
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ set_input_port_default_value(2, Vector3());
+ set_input_port_default_value(3, Vector3());
+}
+
+////////////// Vector Decompose
+String VisualShaderNodeVectorDecompose::get_caption() const {
+ return "VectorDecompose";
+}
+
+int VisualShaderNodeVectorDecompose::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const {
+ return "vec";
+}
+
+int VisualShaderNodeVectorDecompose::get_output_port_count() const {
+ return 3;
+}
+VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else {
+ return "z";
+ }
+}
+
+String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ return code;
+}
+
+VisualShaderNodeVectorDecompose::VisualShaderNodeVectorDecompose() {
+ set_input_port_default_value(0, Vector3());
+}
+
+////////////// Transform Decompose
+
+String VisualShaderNodeTransformDecompose::get_caption() const {
+ return "TransformDecompose";
+}
+
+int VisualShaderNodeTransformDecompose::get_input_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_input_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformDecompose::get_input_port_name(int p_port) const {
+ return "xform";
+}
+
+int VisualShaderNodeTransformDecompose::get_output_port_count() const {
+ return 4;
+}
+VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "x";
+ } else if (p_port == 1) {
+ return "y";
+ } else if (p_port == 2) {
+ return "z";
+ } else {
+ return "origin";
+ }
+}
+
+String VisualShaderNodeTransformDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code;
+ code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n";
+ code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n";
+ code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n";
+ code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n";
+ return code;
+}
+
+VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() {
+ set_input_port_default_value(0, Transform());
+}
+
+////////////// Scalar Uniform
+
+String VisualShaderNodeScalarUniform::get_caption() const {
+ return "ScalarUniform";
+}
+
+int VisualShaderNodeScalarUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeScalarUniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeScalarUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform float " + get_uniform_name() + ";\n";
+}
+String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() {
+}
+
+////////////// Color Uniform
+
+String VisualShaderNodeColorUniform::get_caption() const {
+ return "ColorUniform";
+}
+
+int VisualShaderNodeColorUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeColorUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+
+ return "uniform vec4 " + get_uniform_name() + " : hint_color;\n";
+}
+
+String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
+ code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
+ return code;
+}
+
+VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() {
+}
+
+////////////// Vector Uniform
+
+String VisualShaderNodeVec3Uniform::get_caption() const {
+ return "VectorUniform";
+}
+
+int VisualShaderNodeVec3Uniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec3Uniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform vec3 " + get_uniform_name() + ";\n";
+}
+
+String VisualShaderNodeVec3Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() {
+}
+
+////////////// Transform Uniform
+
+String VisualShaderNodeTransformUniform::get_caption() const {
+ return "TransformUniform";
+}
+
+int VisualShaderNodeTransformUniform::get_input_port_count() const {
+ return 0;
+}
+VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeTransformUniform::get_output_port_count() const {
+ return 1;
+}
+VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform mat4 " + get_uniform_name() + ";\n";
+}
+
+String VisualShaderNodeTransformUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() {
+}
+
+////////////// Texture Uniform
+
+String VisualShaderNodeTextureUniform::get_caption() const {
+ return "TextureUniform";
+}
+
+int VisualShaderNodeTextureUniform::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "uv" : "lod";
+}
+
+int VisualShaderNodeTextureUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = "uniform sampler2D " + get_uniform_name();
+
+ switch (texture_type) {
+ case TYPE_DATA:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black;\n";
+ else
+ code += ";\n";
+ break;
+ case TYPE_COLOR:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black_albedo;\n";
+ else
+ code += " : hint_albedo;\n";
+ break;
+ case TYPE_NORMALMAP: code += " : hint_normal;\n"; break;
+ case TYPE_ANISO: code += " : hint_aniso;\n"; break;
+ }
+
+ return code;
+}
+
+String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+
+ String id = get_uniform_name();
+ String code = "\t{\n";
+ if (p_input_vars[0] == String()) { //none bound, do nothing
+
+ code += "\t\tvec4 n_tex_read = vec4(0.0);\n";
+ } else if (p_input_vars[1] == String()) {
+ //no lod
+ code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
+ } else {
+ code += "\t\tvec4 n_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ }
+
+ code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n";
+ code += "\t}\n";
+ return code;
+}
+
+void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) {
+
+ texture_type = p_type;
+ emit_changed();
+}
+
+VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_texture_type() const {
+ return texture_type;
+}
+
+void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) {
+ color_default = p_default;
+ emit_changed();
+}
+VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const {
+ return color_default;
+}
+
+Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("texture_type");
+ props.push_back("color_default");
+ return props;
+}
+
+void VisualShaderNodeTextureUniform::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type);
+ ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type);
+
+ ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default);
+ ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default");
+
+ BIND_ENUM_CONSTANT(TYPE_DATA);
+ BIND_ENUM_CONSTANT(TYPE_COLOR);
+ BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+ BIND_ENUM_CONSTANT(TYPE_ANISO);
+
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
+}
+
+VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {
+ texture_type = TYPE_DATA;
+ color_default = COLOR_DEFAULT_WHITE;
+}
+
+////////////// CubeMap Uniform
+
+String VisualShaderNodeCubeMapUniform::get_caption() const {
+ return "CubeMapUniform";
+}
+
+int VisualShaderNodeCubeMapUniform::get_input_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const {
+ return p_port == 0 ? "normal" : "lod";
+}
+
+int VisualShaderNodeCubeMapUniform::get_output_port_count() const {
+ return 2;
+}
+VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const {
+ return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+}
+String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const {
+ return p_port == 0 ? "rgb" : "alpha";
+}
+
+String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const {
+ return String();
+}
+
+VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() {
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
new file mode 100644
index 0000000000..2ede36fbc8
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.h
@@ -0,0 +1,861 @@
+#ifndef VISUAL_SHADER_NODES_H
+#define VISUAL_SHADER_NODES_H
+
+#include "scene/resources/visual_shader.h"
+
+/// CONSTANTS ///
+
+class VisualShaderNodeScalarConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode)
+ float constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_constant(float p_value);
+ float get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarConstant();
+};
+
+class VisualShaderNodeColorConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode)
+ Color constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_constant(Color p_value);
+ Color get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeColorConstant();
+};
+
+class VisualShaderNodeVec3Constant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode)
+ Vector3 constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_constant(Vector3 p_value);
+ Vector3 get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVec3Constant();
+};
+
+class VisualShaderNodeTransformConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode)
+ Transform constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_constant(Transform p_value);
+ Transform get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformConstant();
+};
+
+//////////////////////////////////
+
+class VisualShaderNodeTexture : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTexture, VisualShaderNode)
+ Ref<Texture> texture;
+
+public:
+ enum Source {
+ SOURCE_TEXTURE,
+ SOURCE_SCREEN,
+ SOURCE_2D_TEXTURE,
+ SOURCE_2D_NORMAL
+ };
+
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP
+ };
+
+private:
+ Source source;
+ TextureType texture_type;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_source(Source p_source);
+ Source get_source() const;
+
+ void set_texture(Ref<Texture> p_value);
+ Ref<Texture> get_texture() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const;
+
+ VisualShaderNodeTexture();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType)
+VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source)
+
+//////////////////////////////////
+
+class VisualShaderNodeCubeMap : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode)
+ Ref<CubeMap> cube_map;
+
+public:
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP
+ };
+
+private:
+ TextureType texture_type;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_cube_map(Ref<CubeMap> p_value);
+ Ref<CubeMap> get_cube_map() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeCubeMap();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType)
+///////////////////////////////////////
+
+class VisualShaderNodeScalarOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_POW,
+ OP_MAX,
+ OP_MIN,
+ OP_ATAN2
+ };
+
+protected:
+ Operator op;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeScalarOp::Operator)
+
+class VisualShaderNodeVectorOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_POW,
+ OP_MAX,
+ OP_MIN,
+ OP_CROSS
+
+ };
+
+protected:
+ Operator op;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVectorOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator)
+
+class VisualShaderNodeColorOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeColorOp, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_SCREEN,
+ OP_DIFFERENCE,
+ OP_DARKEN,
+ OP_LIGHTEN,
+ OP_OVERLAY,
+ OP_DODGE,
+ OP_BURN,
+ OP_SOFT_LIGHT,
+ OP_HARD_LIGHT,
+ };
+
+protected:
+ Operator op;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeColorOp();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator)
+
+class VisualShaderNodeTransformMult : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_AxB,
+ OP_BxA,
+ };
+
+protected:
+ Operator op;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformMult();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator)
+
+class VisualShaderNodeTransformVecMult : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode)
+
+public:
+ enum Operator {
+ OP_AxB,
+ OP_BxA,
+ OP_3x3_AxB,
+ OP_3x3_BxA,
+ };
+
+protected:
+ Operator op;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeTransformVecMult();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator)
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_SIN,
+ FUNC_COS,
+ FUNC_TAN,
+ FUNC_ASIN,
+ FUNC_ACOS,
+ FUNC_ATAN,
+ FUNC_SINH,
+ FUNC_COSH,
+ FUNC_TANH,
+ FUNC_LOG,
+ FUNC_EXP,
+ FUNC_SQRT,
+ FUNC_ABS,
+ FUNC_SIGN,
+ FUNC_FLOOR,
+ FUNC_ROUND,
+ FUNC_CEIL,
+ FUNC_FRAC,
+ FUNC_SATURATE,
+ FUNC_NEGATE,
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_func);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeScalarFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function)
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode)
+
+public:
+ enum Function {
+ FUNC_NORMALIZE,
+ FUNC_SATURATE,
+ FUNC_NEGATE,
+ FUNC_RECIPROCAL,
+ FUNC_RGB2HSV,
+ FUNC_HSV2RGB,
+ };
+
+protected:
+ Function func;
+
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_function(Function p_func);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const;
+
+ VisualShaderNodeVectorFunc();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function)
+
+///////////////////////////////////////
+
+class VisualShaderNodeDotProduct : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeDotProduct();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorLen : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorLen();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarInterp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeScalarInterp();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorInterp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorInterp, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorInterp();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorCompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorCompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeTransformCompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformCompose, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeTransformCompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeVectorDecompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVectorDecompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeTransformDecompose : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformDecompose, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeTransformDecompose();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeScalarUniform, VisualShaderNodeUniform)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeScalarUniform();
+};
+
+class VisualShaderNodeColorUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeColorUniform();
+};
+
+class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeVec3Uniform();
+};
+
+class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeTransformUniform();
+};
+
+//////////////////////////////////
+
+class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform)
+public:
+ enum TextureType {
+ TYPE_DATA,
+ TYPE_COLOR,
+ TYPE_NORMALMAP,
+ TYPE_ANISO,
+ };
+
+ enum ColorDefault {
+ COLOR_DEFAULT_WHITE,
+ COLOR_DEFAULT_BLACK
+ };
+
+private:
+ TextureType texture_type;
+ ColorDefault color_default;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ Vector<StringName> get_editable_properties() const;
+
+ void set_texture_type(TextureType p_type);
+ TextureType get_texture_type() const;
+
+ void set_color_default(ColorDefault p_default);
+ ColorDefault get_color_default() const;
+
+ VisualShaderNodeTextureUniform();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType)
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault)
+
+//////////////////////////////////
+
+class VisualShaderNodeCubeMapUniform : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode)
+
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeCubeMapUniform();
+};
+
+#endif // VISUAL_SHADER_NODES_H
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
index 1ca2334392..be36c3b748 100644
--- a/servers/audio/audio_driver_dummy.cpp
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -44,7 +44,7 @@ Error AudioDriverDummy::init() {
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
- int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
+ int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
samples_in = memnew_arr(int32_t, buffer_frames * channels);
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 8ee43ddc32..ceb843c031 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -117,6 +117,10 @@ AudioDriver::AudioDriver() {
_last_mix_time = 0;
_mix_amount = 0;
+
+#ifdef DEBUG_ENABLED
+ prof_time = 0;
+#endif
}
AudioDriver *AudioDriverManager::drivers[MAX_DRIVERS];
@@ -184,6 +188,10 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
int todo = p_frames;
+#ifdef DEBUG_ENABLED
+ uint64_t prof_ticks = OS::get_singleton()->get_ticks_usec();
+#endif
+
if (channel_count != get_channel_count()) {
// Amount of channels changed due to a device change
// reinitialize the buses channels and buffers
@@ -235,27 +243,15 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
to_mix -= to_copy;
}
-#ifdef DEBUG_ENABLED
- if (OS::get_singleton() && OS::get_singleton()->is_stdout_verbose()) {
- static uint64_t first_ticks = 0;
- static uint64_t last_ticks = 0;
- static uint64_t ticks = 0;
- static int count = 0;
- static int total = 0;
-
- ticks = OS::get_singleton()->get_ticks_msec();
- if ((ticks - first_ticks) > 10 * 1000 && count > 0) {
- print_line("Audio Driver " + String(AudioDriver::get_singleton()->get_name()) + " average latency: " + itos(total / count) + "ms (frame=" + itos(p_frames) + ")");
- first_ticks = ticks;
- total = 0;
- count = 0;
- }
-
- total += ticks - last_ticks;
- count++;
-
- last_ticks = ticks;
+ // Calculate latency for Performance.AUDIO_OUTPUT_LATENCY
+ if (OS::get_singleton()) {
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ output_latency = (ticks - output_latency_ticks) / 1000000.f;
+ output_latency_ticks = ticks;
}
+
+#ifdef DEBUG_ENABLED
+ prof_time += OS::get_singleton()->get_ticks_usec() - prof_ticks;
#endif
}
@@ -330,6 +326,10 @@ void AudioServer::_mix_step() {
if (!bus->effects[j].enabled)
continue;
+#ifdef DEBUG_ENABLED
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+#endif
+
for (int k = 0; k < bus->channels.size(); k++) {
if (!bus->channels[k].active)
@@ -344,6 +344,10 @@ void AudioServer::_mix_step() {
continue;
SWAP(bus->channels[k].buffer, temp_buffer[k]);
}
+
+#ifdef DEBUG_ENABLED
+ bus->effects[j].prof_time += OS::get_singleton()->get_ticks_usec() - ticks;
+#endif
}
}
@@ -784,6 +788,9 @@ void AudioServer::add_bus_effect(int p_bus, const Ref<AudioEffect> &p_effect, in
fx.effect = p_effect;
//fx.instance=p_effect->instance();
fx.enabled = true;
+#ifdef DEBUG_ENABLED
+ fx.prof_time = 0;
+#endif
if (p_at_pos >= buses[p_bus]->effects.size() || p_at_pos < 0) {
buses[p_bus]->effects.push_back(fx);
@@ -896,8 +903,8 @@ void AudioServer::init_channels_and_buffers() {
void AudioServer::init() {
- channel_disable_threshold_db = GLOBAL_DEF("audio/channel_disable_threshold_db", -60.0);
- channel_disable_frames = float(GLOBAL_DEF("audio/channel_disable_time", 2.0)) * get_mix_rate();
+ channel_disable_threshold_db = GLOBAL_DEF_RST("audio/channel_disable_threshold_db", -60.0);
+ channel_disable_frames = float(GLOBAL_DEF_RST("audio/channel_disable_time", 2.0)) * get_mix_rate();
buffer_size = 1024; //hardcoded for now
init_channels_and_buffers();
@@ -913,7 +920,69 @@ void AudioServer::init() {
set_edited(false); //avoid editors from thinking this was edited
#endif
- GLOBAL_DEF("audio/video_delay_compensation_ms", 0);
+ GLOBAL_DEF_RST("audio/video_delay_compensation_ms", 0);
+}
+
+void AudioServer::update() {
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
+
+ // Driver time includes server time + effects times
+ // Server time includes effects times
+ uint64_t driver_time = AudioDriver::get_singleton()->get_profiling_time();
+ uint64_t server_time = prof_time;
+
+ // Substract the server time from the driver time
+ if (driver_time > server_time)
+ driver_time -= server_time;
+
+ Array values;
+
+ for (int i = buses.size() - 1; i >= 0; i--) {
+ Bus *bus = buses[i];
+ if (bus->bypass)
+ continue;
+
+ for (int j = 0; j < bus->effects.size(); j++) {
+ if (!bus->effects[j].enabled)
+ continue;
+
+ values.push_back(String(bus->name) + bus->effects[j].effect->get_name());
+ values.push_back(USEC_TO_SEC(bus->effects[j].prof_time));
+
+ // Substract the effect time from the driver and server times
+ if (driver_time > bus->effects[j].prof_time)
+ driver_time -= bus->effects[j].prof_time;
+ if (server_time > bus->effects[j].prof_time)
+ server_time -= bus->effects[j].prof_time;
+ }
+ }
+
+ values.push_back("audio_server");
+ values.push_back(USEC_TO_SEC(server_time));
+ values.push_back("audio_driver");
+ values.push_back(USEC_TO_SEC(driver_time));
+
+ ScriptDebugger::get_singleton()->add_profiling_frame_data("audio_thread", values);
+ }
+
+ // Reset profiling times
+ for (int i = buses.size() - 1; i >= 0; i--) {
+ Bus *bus = buses[i];
+ if (bus->bypass)
+ continue;
+
+ for (int j = 0; j < bus->effects.size(); j++) {
+ if (!bus->effects[j].enabled)
+ continue;
+
+ bus->effects[j].prof_time = 0;
+ }
+ }
+
+ AudioDriver::get_singleton()->reset_profiling_time();
+ prof_time = 0;
+#endif
}
void AudioServer::load_default_bus_layout() {
@@ -1201,6 +1270,11 @@ AudioServer::AudioServer() {
mix_frames = 0;
channel_count = 0;
to_mix = 0;
+ output_latency = 0;
+ output_latency_ticks = 0;
+#ifdef DEBUG_ENABLED
+ prof_time = 0;
+#endif
}
AudioServer::~AudioServer() {
diff --git a/servers/audio_server.h b/servers/audio_server.h
index b7fcd9c093..258fd1d9b0 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -33,6 +33,7 @@
#include "audio_frame.h"
#include "object.h"
+#include "os/os.h"
#include "servers/audio/audio_effect.h"
#include "variant.h"
@@ -44,10 +45,23 @@ class AudioDriver {
uint64_t _last_mix_time;
uint64_t _mix_amount;
+#ifdef DEBUG_ENABLED
+ uint64_t prof_ticks;
+ uint64_t prof_time;
+#endif
+
protected:
void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true);
void update_mix_time(int p_frames);
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void start_counting_ticks() { prof_ticks = OS::get_singleton()->get_ticks_usec(); }
+ _FORCE_INLINE_ void stop_counting_ticks() { prof_time += OS::get_singleton()->get_ticks_usec() - prof_ticks; }
+#else
+ _FORCE_INLINE_ void start_counting_ticks() {}
+ _FORCE_INLINE_ void stop_counting_ticks() {}
+#endif
+
public:
double get_mix_time() const; //useful for video -> audio sync
@@ -82,6 +96,11 @@ public:
SpeakerMode get_speaker_mode_by_total_channels(int p_channels) const;
int get_total_channels_by_speaker_mode(SpeakerMode) const;
+#ifdef DEBUG_ENABLED
+ uint64_t get_profiling_time() const { return prof_time; }
+ void reset_profiling_time() { prof_time = 0; }
+#endif
+
AudioDriver();
virtual ~AudioDriver() {}
};
@@ -129,6 +148,9 @@ private:
uint32_t buffer_size;
uint64_t mix_count;
uint64_t mix_frames;
+#ifdef DEBUG_ENABLED
+ uint64_t prof_time;
+#endif
float channel_disable_threshold_db;
uint32_t channel_disable_frames;
@@ -166,6 +188,9 @@ private:
struct Effect {
Ref<AudioEffect> effect;
bool enabled;
+#ifdef DEBUG_ENABLED
+ uint64_t prof_time;
+#endif
};
Vector<Effect> effects;
@@ -190,6 +215,9 @@ private:
Mutex *audio_data_lock;
+ float output_latency;
+ uint64_t output_latency_ticks;
+
void init_channels_and_buffers();
void _mix_step();
@@ -273,6 +301,7 @@ public:
virtual void init();
virtual void finish();
+ virtual void update();
virtual void load_default_bus_layout();
/* MISC config */
@@ -306,6 +335,8 @@ public:
String get_device();
void set_device(String device);
+ float get_output_latency() { return output_latency; }
+
AudioServer();
virtual ~AudioServer();
};
diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp
index 2a6a9e08ae..5a41b621eb 100644
--- a/servers/physics/body_pair_sw.cpp
+++ b/servers/physics/body_pair_sw.cpp
@@ -211,6 +211,44 @@ bool BodyPairSW::_test_ccd(real_t p_step, BodySW *p_A, int p_shape_A, const Tran
return true;
}
+real_t combine_bounce(BodySW *A, BodySW *B) {
+ const PhysicsServer::CombineMode cm = A->get_bounce_combine_mode();
+
+ switch (cm) {
+ case PhysicsServer::COMBINE_MODE_INHERIT:
+ if (B->get_bounce_combine_mode() != PhysicsServer::COMBINE_MODE_INHERIT)
+ return combine_bounce(B, A);
+ // else use MAX [This is used when the two bodies doesn't use physical material]
+ case PhysicsServer::COMBINE_MODE_MAX:
+ return MAX(A->get_bounce(), B->get_bounce());
+ case PhysicsServer::COMBINE_MODE_MIN:
+ return MIN(A->get_bounce(), B->get_bounce());
+ case PhysicsServer::COMBINE_MODE_MULTIPLY:
+ return A->get_bounce() * B->get_bounce();
+ default: // Is always PhysicsServer::COMBINE_MODE_AVERAGE:
+ return (A->get_bounce() + B->get_bounce()) / 2;
+ }
+}
+
+real_t combine_friction(BodySW *A, BodySW *B) {
+ const PhysicsServer::CombineMode cm = A->get_friction_combine_mode();
+
+ switch (cm) {
+ case PhysicsServer::COMBINE_MODE_INHERIT:
+ if (B->get_friction_combine_mode() != PhysicsServer::COMBINE_MODE_INHERIT)
+ return combine_friction(B, A);
+ // else use Multiply [This is used when the two bodies doesn't use physical material]
+ case PhysicsServer::COMBINE_MODE_MULTIPLY:
+ return A->get_friction() * B->get_friction();
+ case PhysicsServer::COMBINE_MODE_MAX:
+ return MAX(A->get_friction(), B->get_friction());
+ case PhysicsServer::COMBINE_MODE_MIN:
+ return MIN(A->get_friction(), B->get_friction());
+ default: // Is always PhysicsServer::COMBINE_MODE_AVERAGE:
+ return (A->get_friction() + B->get_friction()) / 2;
+ }
+}
+
bool BodyPairSW::setup(real_t p_step) {
//cannot collide
@@ -331,7 +369,7 @@ bool BodyPairSW::setup(real_t p_step) {
c.acc_bias_impulse = 0;
c.acc_bias_impulse_center_of_mass = 0;
- c.bounce = MAX(A->get_bounce(), B->get_bounce());
+ c.bounce = combine_bounce(A, B);
if (c.bounce) {
Vector3 crA = A->get_angular_velocity().cross(c.rA);
@@ -421,7 +459,7 @@ void BodyPairSW::solve(real_t p_step) {
//friction impulse
- real_t friction = A->get_friction() * B->get_friction();
+ real_t friction = combine_friction(A, B);
Vector3 lvA = A->get_linear_velocity() + A->get_angular_velocity().cross(c.rA);
Vector3 lvB = B->get_linear_velocity() + B->get_angular_velocity().cross(c.rB);
diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp
index cc9681193c..59f987fc17 100644
--- a/servers/physics/body_sw.cpp
+++ b/servers/physics/body_sw.cpp
@@ -423,6 +423,22 @@ void BodySW::_compute_area_gravity_and_dampenings(const AreaSW *p_area) {
area_angular_damp += p_area->get_angular_damp();
}
+void BodySW::set_combine_mode(PhysicsServer::BodyParameter p_param, PhysicsServer::CombineMode p_mode) {
+ if (p_param == PhysicsServer::BODY_PARAM_BOUNCE) {
+ bounce_combine_mode = p_mode;
+ } else {
+ friction_combine_mode = p_mode;
+ }
+}
+
+PhysicsServer::CombineMode BodySW::get_combine_mode(PhysicsServer::BodyParameter p_param) const {
+ if (p_param == PhysicsServer::BODY_PARAM_BOUNCE) {
+ return bounce_combine_mode;
+ } else {
+ return friction_combine_mode;
+ }
+}
+
void BodySW::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool lock) {
if (lock) {
locked_axis |= p_axis;
diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h
index fd2ab16b84..25eb20f5d8 100644
--- a/servers/physics/body_sw.h
+++ b/servers/physics/body_sw.h
@@ -49,6 +49,8 @@ class BodySW : public CollisionObjectSW {
real_t mass;
real_t bounce;
real_t friction;
+ PhysicsServer::CombineMode bounce_combine_mode;
+ PhysicsServer::CombineMode friction_combine_mode;
real_t linear_damp;
real_t angular_damp;
@@ -298,6 +300,12 @@ public:
_FORCE_INLINE_ Vector3 get_gravity() const { return gravity; }
_FORCE_INLINE_ real_t get_bounce() const { return bounce; }
+ void set_combine_mode(PhysicsServer::BodyParameter p_param, PhysicsServer::CombineMode p_mode);
+ PhysicsServer::CombineMode get_combine_mode(PhysicsServer::BodyParameter p_param) const;
+
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_bounce_combine_mode() const { return bounce_combine_mode; }
+ _FORCE_INLINE_ PhysicsServer::CombineMode get_friction_combine_mode() const { return friction_combine_mode; }
+
void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool lock);
bool is_axis_locked(PhysicsServer::BodyAxis p_axis) const;
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index f2dbb635f8..f1e0cbef29 100644
--- a/servers/physics/physics_server_sw.cpp
+++ b/servers/physics/physics_server_sw.cpp
@@ -65,6 +65,11 @@ RID PhysicsServerSW::shape_create(ShapeType p_shape) {
shape = memnew(CapsuleShapeSW);
} break;
+ case SHAPE_CYLINDER: {
+
+ ERR_EXPLAIN("CylinderShape is not supported in GodotPhysics. Please switch to Bullet in the Project Settings.");
+ ERR_FAIL_V(RID());
+ } break;
case SHAPE_CONVEX_POLYGON: {
shape = memnew(ConvexPolygonShapeSW);
@@ -696,6 +701,20 @@ real_t PhysicsServerSW::body_get_param(RID p_body, BodyParameter p_param) const
return body->get_param(p_param);
};
+void PhysicsServerSW::body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode) {
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_combine_mode(p_param, p_mode);
+}
+
+PhysicsServer::CombineMode PhysicsServerSW::body_get_combine_mode(RID p_body, BodyParameter p_param) const {
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, COMBINE_MODE_INHERIT);
+
+ return body->get_combine_mode(p_param);
+}
+
void PhysicsServerSW::body_set_kinematic_safe_margin(RID p_body, real_t p_margin) {
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -1362,6 +1381,8 @@ void PhysicsServerSW::init() {
void PhysicsServerSW::step(real_t p_step) {
+#ifndef _3D_DISABLED
+
if (!active)
return;
@@ -1382,6 +1403,7 @@ void PhysicsServerSW::step(real_t p_step) {
active_objects += E->get()->get_active_objects();
collision_pairs += E->get()->get_collision_pairs();
}
+#endif
}
void PhysicsServerSW::sync(){
@@ -1390,6 +1412,8 @@ void PhysicsServerSW::sync(){
void PhysicsServerSW::flush_queries() {
+#ifndef _3D_DISABLED
+
if (!active)
return;
@@ -1436,6 +1460,7 @@ void PhysicsServerSW::flush_queries() {
ScriptDebugger::get_singleton()->add_profiling_frame_data("physics", values);
}
+#endif
};
void PhysicsServerSW::finish() {
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
index 3f56ba26d0..7e9700d026 100644
--- a/servers/physics/physics_server_sw.h
+++ b/servers/physics/physics_server_sw.h
@@ -188,6 +188,10 @@ public:
virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value);
virtual real_t body_get_param(RID p_body, BodyParameter p_param) const;
+ /// p_param accept only Bounce and Friction
+ virtual void body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode);
+ virtual CombineMode body_get_combine_mode(RID p_body, BodyParameter p_param) const;
+
virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin);
virtual real_t body_get_kinematic_safe_margin(RID p_body) const;
@@ -230,6 +234,72 @@ public:
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body);
+ /* SOFT BODY */
+
+ virtual RID soft_body_create(bool p_init_sleeping = false) { return RID(); }
+
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {}
+
+ virtual void soft_body_set_space(RID p_body, RID p_space) {}
+ virtual RID soft_body_get_space(RID p_body) const { return RID(); }
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {}
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const { return 0; }
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {}
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const { return 0; }
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) {}
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) {}
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {}
+
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {}
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const { return Variant(); }
+
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) {}
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const { return Vector3(); }
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) {}
+ virtual bool soft_body_is_ray_pickable(RID p_body) const { return false; }
+
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {}
+ virtual int soft_body_get_simulation_precision(RID p_body) { return 0; }
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) {}
+ virtual real_t soft_body_get_total_mass(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_volume_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {}
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {}
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {}
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {}
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) {}
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {}
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) { return Vector3(); }
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const { return Vector3(); }
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body) {}
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {}
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) { return 0; }
+
/* JOINT API */
virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B);
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
index aa063d6c1e..fcd2a65ee7 100644
--- a/servers/physics_2d/body_2d_sw.cpp
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -405,6 +405,22 @@ void Body2DSW::_compute_area_gravity_and_dampenings(const Area2DSW *p_area) {
area_angular_damp += p_area->get_angular_damp();
}
+void Body2DSW::set_combine_mode(Physics2DServer::BodyParameter p_param, Physics2DServer::CombineMode p_mode) {
+ if (p_param == Physics2DServer::BODY_PARAM_BOUNCE) {
+ bounce_combine_mode = p_mode;
+ } else {
+ friction_combine_mode = p_mode;
+ }
+}
+
+Physics2DServer::CombineMode Body2DSW::get_combine_mode(Physics2DServer::BodyParameter p_param) const {
+ if (p_param == Physics2DServer::BODY_PARAM_BOUNCE) {
+ return bounce_combine_mode;
+ } else {
+ return friction_combine_mode;
+ }
+}
+
void Body2DSW::integrate_forces(real_t p_step) {
if (mode == Physics2DServer::BODY_MODE_STATIC)
diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h
index 782adf3416..301bd6b299 100644
--- a/servers/physics_2d/body_2d_sw.h
+++ b/servers/physics_2d/body_2d_sw.h
@@ -54,6 +54,8 @@ class Body2DSW : public CollisionObject2DSW {
real_t mass;
real_t bounce;
real_t friction;
+ Physics2DServer::CombineMode bounce_combine_mode;
+ Physics2DServer::CombineMode friction_combine_mode;
real_t _inv_mass;
real_t _inv_inertia;
@@ -256,6 +258,12 @@ public:
_FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; }
_FORCE_INLINE_ real_t get_angular_damp() const { return angular_damp; }
+ void set_combine_mode(Physics2DServer::BodyParameter p_param, Physics2DServer::CombineMode p_mode);
+ Physics2DServer::CombineMode get_combine_mode(Physics2DServer::BodyParameter p_param) const;
+
+ _FORCE_INLINE_ Physics2DServer::CombineMode get_bounce_combine_mode() const { return bounce_combine_mode; }
+ _FORCE_INLINE_ Physics2DServer::CombineMode get_friction_combine_mode() const { return friction_combine_mode; }
+
void integrate_forces(real_t p_step);
void integrate_velocities(real_t p_step);
diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp
index 61c0e0063f..be8dcf6fa8 100644
--- a/servers/physics_2d/body_pair_2d_sw.cpp
+++ b/servers/physics_2d/body_pair_2d_sw.cpp
@@ -219,6 +219,44 @@ bool BodyPair2DSW::_test_ccd(real_t p_step, Body2DSW *p_A, int p_shape_A, const
return true;
}
+real_t combine_bounce(Body2DSW *A, Body2DSW *B) {
+ const Physics2DServer::CombineMode cm = A->get_bounce_combine_mode();
+
+ switch (cm) {
+ case Physics2DServer::COMBINE_MODE_INHERIT:
+ if (B->get_bounce_combine_mode() != Physics2DServer::COMBINE_MODE_INHERIT)
+ return combine_bounce(B, A);
+ // else use MAX [This is used when the two bodies doesn't use physical material]
+ case Physics2DServer::COMBINE_MODE_MAX:
+ return MAX(A->get_bounce(), B->get_bounce());
+ case Physics2DServer::COMBINE_MODE_MIN:
+ return MIN(A->get_bounce(), B->get_bounce());
+ case Physics2DServer::COMBINE_MODE_MULTIPLY:
+ return A->get_bounce() * B->get_bounce();
+ default: // Is always Physics2DServer::COMBINE_MODE_AVERAGE:
+ return (A->get_bounce() + B->get_bounce()) / 2;
+ }
+}
+
+real_t combine_friction(Body2DSW *A, Body2DSW *B) {
+ const Physics2DServer::CombineMode cm = A->get_friction_combine_mode();
+
+ switch (cm) {
+ case Physics2DServer::COMBINE_MODE_INHERIT:
+ if (B->get_friction_combine_mode() != Physics2DServer::COMBINE_MODE_INHERIT)
+ return combine_friction(B, A);
+ // else use Multiply [This is used when the two bodies doesn't use physical material]
+ case Physics2DServer::COMBINE_MODE_MULTIPLY:
+ return A->get_friction() * B->get_friction();
+ case Physics2DServer::COMBINE_MODE_MAX:
+ return MAX(A->get_friction(), B->get_friction());
+ case Physics2DServer::COMBINE_MODE_MIN:
+ return MIN(A->get_friction(), B->get_friction());
+ default: // Is always Physics2DServer::COMBINE_MODE_AVERAGE:
+ return (A->get_friction() + B->get_friction()) / 2;
+ }
+}
+
bool BodyPair2DSW::setup(real_t p_step) {
//cannot collide
@@ -432,7 +470,7 @@ bool BodyPair2DSW::setup(real_t p_step) {
#endif
- c.bounce = MAX(A->get_bounce(), B->get_bounce());
+ c.bounce = combine_bounce(A, B);
if (c.bounce) {
Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
@@ -488,7 +526,7 @@ void BodyPair2DSW::solve(real_t p_step) {
real_t jnOld = c.acc_normal_impulse;
c.acc_normal_impulse = MAX(jnOld + jn, 0.0f);
- real_t friction = A->get_friction() * B->get_friction();
+ real_t friction = combine_friction(A, B);
real_t jtMax = friction * c.acc_normal_impulse;
real_t jt = -vt * c.mass_tangent;
diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp
index efee98a35a..6ce019f36e 100644
--- a/servers/physics_2d/collision_solver_2d_sw.cpp
+++ b/servers/physics_2d/collision_solver_2d_sw.cpp
@@ -72,7 +72,7 @@ bool CollisionSolver2DSW::solve_static_line(const Shape2DSW *p_shape_A, const Tr
return found;
}
-bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis) {
+bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis) {
const RayShape2DSW *ray = static_cast<const RayShape2DSW *>(p_shape_A);
if (p_shape_B->get_type() == Physics2DServer::SHAPE_RAY)
@@ -80,6 +80,11 @@ bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Transf
Vector2 from = p_transform_A.get_origin();
Vector2 to = from + p_transform_A[1] * ray->get_length();
+ if (p_motion_A != Vector2()) {
+ //not the best but should be enough
+ Vector2 normal = (to - from).normalized();
+ to += normal * MAX(0.0, normal.dot(p_motion_A));
+ }
Vector2 support_A = to;
Transform2D invb = p_transform_B.affine_inverse();
@@ -270,9 +275,9 @@ bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p
}
if (swap) {
- return solve_raycast(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, sep_axis);
+ return solve_raycast(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, sep_axis);
} else {
- return solve_raycast(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, sep_axis);
+ return solve_raycast(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, sep_axis);
}
} else if (concave_B) {
diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h
index e39c41fb75..6faa166115 100644
--- a/servers/physics_2d/collision_solver_2d_sw.h
+++ b/servers/physics_2d/collision_solver_2d_sw.h
@@ -41,7 +41,7 @@ private:
static bool solve_static_line(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
static void concave_callback(void *p_userdata, Shape2DSW *p_convex);
static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL, real_t p_margin_A = 0, real_t p_margin_B = 0);
- static bool solve_raycast(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL);
+ static bool solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL);
public:
static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis = NULL, real_t p_margin_A = 0, real_t p_margin_B = 0);
diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp
index a14fed8184..cfcef8cb04 100644
--- a/servers/physics_2d/physics_2d_server_sw.cpp
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -789,6 +789,22 @@ real_t Physics2DServerSW::body_get_param(RID p_body, BodyParameter p_param) cons
return body->get_param(p_param);
};
+void Physics2DServerSW::body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_combine_mode(p_param, p_mode);
+}
+
+Physics2DServer::CombineMode Physics2DServerSW::body_get_combine_mode(RID p_body, BodyParameter p_param) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, COMBINE_MODE_INHERIT);
+
+ return body->get_combine_mode(p_param);
+}
+
void Physics2DServerSW::body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {
Body2DSW *body = body_owner.get(p_body);
@@ -962,22 +978,40 @@ void Physics2DServerSW::body_set_pickable(RID p_body, bool p_pickable) {
body->set_pickable(p_pickable);
}
-bool Physics2DServerSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result) {
+bool Physics2DServerSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+ ERR_FAIL_COND_V(!body->get_space(), false);
+ ERR_FAIL_COND_V(body->get_space()->is_locked(), false);
+
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes);
+}
+
+int Physics2DServerSW::body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) {
Body2DSW *body = body_owner.get(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
ERR_FAIL_COND_V(body->get_space()->is_locked(), false);
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result);
+ return body->get_space()->test_body_ray_separation(body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin);
}
Physics2DDirectBodyState *Physics2DServerSW::body_get_direct_state(RID p_body) {
+ if ((using_threads && !doing_sync)) {
+ ERR_EXPLAIN("Body state is inaccessible right now, wait for iteration or physics process notification.");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (!body_owner.owns(p_body))
+ return NULL;
+
Body2DSW *body = body_owner.get(p_body);
ERR_FAIL_COND_V(!body, NULL);
- if ((using_threads && !doing_sync) || body->get_space()->is_locked()) {
+ if (body->get_space()->is_locked()) {
ERR_EXPLAIN("Body state is inaccessible right now, wait for iteration or physics process notification.");
ERR_FAIL_V(NULL);
diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h
index 036eb934e1..bf00746063 100644
--- a/servers/physics_2d/physics_2d_server_sw.h
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -200,6 +200,10 @@ public:
virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value);
virtual real_t body_get_param(RID p_body, BodyParameter p_param) const;
+ /// p_param accept only Bounce and Friction
+ virtual void body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode);
+ virtual CombineMode body_get_combine_mode(RID p_body, BodyParameter p_param) const;
+
virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
virtual Variant body_get_state(RID p_body, BodyState p_state) const;
@@ -232,7 +236,8 @@ public:
virtual void body_set_pickable(RID p_body, bool p_pickable);
- virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL);
+ virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true);
+ virtual int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001);
// this function only works on physics process, errors and returns null otherwise
virtual Physics2DDirectBodyState *body_get_direct_state(RID p_body);
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index a15e8bde8b..a85cd5ef8d 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -211,6 +211,9 @@ public:
FUNC3(body_set_param, RID, BodyParameter, real_t);
FUNC2RC(real_t, body_get_param, RID, BodyParameter);
+ FUNC3(body_set_combine_mode, RID, BodyParameter, CombineMode);
+ FUNC2RC(CombineMode, body_get_combine_mode, RID, BodyParameter);
+
FUNC3(body_set_state, RID, BodyState, const Variant &);
FUNC2RC(Variant, body_get_state, RID, BodyState);
@@ -245,10 +248,16 @@ public:
FUNC2(body_set_pickable, RID, bool);
- bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL) {
+ bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true) {
+
+ ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes);
+ }
+
+ int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) {
ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
- return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result);
+ return physics_2d_server->body_test_ray_separation(p_body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin);
}
// this function only works on physics process, errors and returns null otherwise
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index 0e1f74d8d0..6e45951f42 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -487,7 +487,156 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) {
return amount;
}
-bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result) {
+int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, Physics2DServer::SeparationResult *r_results, int p_result_max, real_t p_margin) {
+
+ Rect2 body_aabb;
+
+ for (int i = 0; i < p_body->get_shape_count(); i++) {
+
+ if (i == 0)
+ body_aabb = p_body->get_shape_aabb(i);
+ else
+ body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
+
+ // Undo the currently transform the physics server is aware of and apply the provided one
+ body_aabb = p_transform.xform(p_body->get_inv_transform().xform(body_aabb));
+ body_aabb = body_aabb.grow(p_margin);
+
+ Transform2D body_transform = p_transform;
+
+ for (int i = 0; i < p_result_max; i++) {
+ //reset results
+ r_results[i].collision_depth = 0;
+ }
+
+ int rays_found = 0;
+
+ {
+ // raycast AND separate
+
+ const int max_results = 32;
+ int recover_attempts = 4;
+ Vector2 sr[max_results * 2];
+ Physics2DServerSW::CollCbkData cbk;
+ cbk.max = max_results;
+ Physics2DServerSW::CollCbkData *cbkptr = &cbk;
+ CollisionSolver2DSW::CallbackResult cbkres = Physics2DServerSW::_shape_col_cbk;
+
+ do {
+
+ Vector2 recover_motion;
+
+ bool collided = false;
+
+ int amount = _cull_aabb_for_body(p_body, body_aabb);
+ int ray_index = 0;
+
+ for (int j = 0; j < p_body->get_shape_count(); j++) {
+ if (p_body->is_shape_set_as_disabled(j))
+ continue;
+
+ Shape2DSW *body_shape = p_body->get_shape(j);
+
+ if (body_shape->get_type() != Physics2DServer::SHAPE_RAY)
+ continue;
+
+ Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
+
+ for (int i = 0; i < amount; i++) {
+
+ const CollisionObject2DSW *col_obj = intersection_query_results[i];
+ int shape_idx = intersection_query_subindex_results[i];
+
+ cbk.amount = 0;
+ cbk.ptr = sr;
+ cbk.invalid_by_dir = 0;
+
+ if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) {
+ const Body2DSW *b = static_cast<const Body2DSW *>(col_obj);
+ if (p_infinite_inertia && Physics2DServer::BODY_MODE_STATIC != b->get_mode() && Physics2DServer::BODY_MODE_KINEMATIC != b->get_mode()) {
+ continue;
+ }
+ }
+
+ if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
+
+ cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ cbk.valid_depth = p_margin; //only valid depth is the collision margin
+ cbk.invalid_by_dir = 0;
+
+ } else {
+ cbk.valid_dir = Vector2();
+ cbk.valid_depth = 0;
+ cbk.invalid_by_dir = 0;
+ }
+
+ Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
+ if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) {
+ if (cbk.amount > 0) {
+ collided = true;
+ }
+
+ if (ray_index < p_result_max) {
+ Physics2DServer::SeparationResult &result = r_results[ray_index];
+
+ for (int k = 0; k < cbk.amount; k++) {
+ Vector2 a = sr[k * 2 + 0];
+ Vector2 b = sr[k * 2 + 1];
+
+ recover_motion += (b - a) * 0.4;
+
+ float depth = a.distance_to(b);
+ if (depth > result.collision_depth) {
+
+ result.collision_depth = depth;
+ result.collision_point = b;
+ result.collision_normal = (b - a).normalized();
+ result.collision_local_shape = shape_idx;
+ result.collider = col_obj->get_self();
+ result.collider_id = col_obj->get_instance_id();
+ result.collider_metadata = col_obj->get_shape_metadata(shape_idx);
+ if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
+ Body2DSW *body = (Body2DSW *)col_obj;
+
+ Vector2 rel_vec = b - body->get_transform().get_origin();
+ result.collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ray_index++;
+ }
+
+ rays_found = MAX(ray_index, rays_found);
+
+ if (!collided || recover_motion == Vector2()) {
+ break;
+ }
+
+ body_transform.elements[2] += recover_motion;
+ body_aabb.position += recover_motion;
+
+ recover_attempts--;
+ } while (recover_attempts);
+ }
+
+ //optimize results (remove non colliding)
+ for (int i = 0; i < rays_found; i++) {
+ if (r_results[i].collision_depth == 0) {
+ rays_found--;
+ SWAP(r_results[i], r_results[rays_found]);
+ }
+ }
+
+ r_recover_motion = body_transform.elements[2] - p_transform.elements[2];
+ return rays_found;
+}
+
+bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result, bool p_exclude_raycast_shapes) {
//give me back regular physics engine logic
//this is madness
@@ -547,8 +696,12 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
if (p_body->is_shape_set_as_disabled(j))
continue;
- Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
Shape2DSW *body_shape = p_body->get_shape(j);
+ if (p_exclude_raycast_shapes && body_shape->get_type() == Physics2DServer::SHAPE_RAY) {
+ continue;
+ }
+
+ Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
for (int i = 0; i < amount; i++) {
const CollisionObject2DSW *col_obj = intersection_query_results[i];
@@ -635,8 +788,12 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
if (p_body->is_shape_set_as_disabled(body_shape_idx))
continue;
- Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx);
Shape2DSW *body_shape = p_body->get_shape(body_shape_idx);
+ if (p_exclude_raycast_shapes && body_shape->get_type() == Physics2DServer::SHAPE_RAY) {
+ continue;
+ }
+
+ Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx);
bool stuck = false;
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
index 79349c46f3..959e15e12d 100644
--- a/servers/physics_2d/space_2d_sw.h
+++ b/servers/physics_2d/space_2d_sw.h
@@ -182,7 +182,8 @@ public:
int get_collision_pairs() const { return collision_pairs; }
- bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result);
+ bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result, bool p_exclude_raycast_shapes = true);
+ int test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, Physics2DServer::SeparationResult *r_results, int p_result_max, real_t p_margin);
void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
_FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.empty(); }
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index ba5232f7fe..89c649af49 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -416,6 +416,19 @@ public:
virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value) = 0;
virtual float body_get_param(RID p_body, BodyParameter p_param) const = 0;
+ enum CombineMode {
+ COMBINE_MODE_MAX,
+ COMBINE_MODE_MIN,
+ COMBINE_MODE_MULTIPLY,
+ COMBINE_MODE_AVERAGE,
+
+ COMBINE_MODE_INHERIT /// Inherit from other body or use COMBINE_MODE_MAX (Restitution) COMBINE_MODE_MULTIPLY (Friction)
+ };
+
+ /// p_param accept only Bounce and Friction
+ virtual void body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode) = 0;
+ virtual CombineMode body_get_combine_mode(RID p_body, BodyParameter p_param) const = 0;
+
//state
enum BodyState {
BODY_STATE_TRANSFORM,
@@ -479,7 +492,22 @@ public:
Variant collider_metadata;
};
- virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.001, MotionResult *r_result = NULL) = 0;
+ virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true) = 0;
+
+ struct SeparationResult {
+
+ float collision_depth;
+ Vector2 collision_point;
+ Vector2 collision_normal;
+ Vector2 collider_velocity;
+ int collision_local_shape;
+ ObjectID collider_id;
+ RID collider;
+ int collider_shape;
+ Variant collider_metadata;
+ };
+
+ virtual int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) = 0;
/* JOINT API */
diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp
index 82c4eb2e13..b4bd4cb35f 100644
--- a/servers/physics_server.cpp
+++ b/servers/physics_server.cpp
@@ -400,6 +400,8 @@ void PhysicsShapeQueryResult::_bind_methods() {
void PhysicsServer::_bind_methods() {
+#ifndef _3D_DISABLED
+
ClassDB::bind_method(D_METHOD("shape_create", "type"), &PhysicsServer::shape_create);
ClassDB::bind_method(D_METHOD("shape_set_data", "shape", "data"), &PhysicsServer::shape_set_data);
@@ -664,6 +666,7 @@ void PhysicsServer::_bind_methods() {
BIND_ENUM_CONSTANT(SHAPE_SPHERE);
BIND_ENUM_CONSTANT(SHAPE_BOX);
BIND_ENUM_CONSTANT(SHAPE_CAPSULE);
+ BIND_ENUM_CONSTANT(SHAPE_CYLINDER);
BIND_ENUM_CONSTANT(SHAPE_CONVEX_POLYGON);
BIND_ENUM_CONSTANT(SHAPE_CONCAVE_POLYGON);
BIND_ENUM_CONSTANT(SHAPE_HEIGHTMAP);
@@ -736,6 +739,8 @@ void PhysicsServer::_bind_methods() {
BIND_ENUM_CONSTANT(BODY_AXIS_ANGULAR_X);
BIND_ENUM_CONSTANT(BODY_AXIS_ANGULAR_Y);
BIND_ENUM_CONSTANT(BODY_AXIS_ANGULAR_Z);
+
+#endif
}
PhysicsServer::PhysicsServer() {
diff --git a/servers/physics_server.h b/servers/physics_server.h
index 6712bee8dc..497d23c555 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -228,6 +228,7 @@ public:
SHAPE_SPHERE, ///< float:"radius"
SHAPE_BOX, ///< vec3:"extents"
SHAPE_CAPSULE, ///< dict( float:"radius", float:"height"):capsule
+ SHAPE_CYLINDER, ///< dict( float:"radius", float:"height"):cylinder
SHAPE_CONVEX_POLYGON, ///< array of planes:"planes"
SHAPE_CONCAVE_POLYGON, ///< vector3 array:"triangles" , or Dictionary with "indices" (int array) and "triangles" (Vector3 array)
SHAPE_HEIGHTMAP, ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
@@ -398,6 +399,19 @@ public:
virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value) = 0;
virtual float body_get_param(RID p_body, BodyParameter p_param) const = 0;
+ enum CombineMode {
+ COMBINE_MODE_MAX,
+ COMBINE_MODE_MIN,
+ COMBINE_MODE_MULTIPLY,
+ COMBINE_MODE_AVERAGE,
+
+ COMBINE_MODE_INHERIT /// Inherit from other body or use COMBINE_MODE_MAX (Restitution) COMBINE_MODE_MULTIPLY (Friction)
+ };
+
+ /// p_param accept only Bounce and Friction
+ virtual void body_set_combine_mode(RID p_body, BodyParameter p_param, CombineMode p_mode) = 0;
+ virtual CombineMode body_get_combine_mode(RID p_body, BodyParameter p_param) const = 0;
+
virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin) = 0;
virtual real_t body_get_kinematic_safe_margin(RID p_body) const = 0;
@@ -476,6 +490,72 @@ public:
virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL) = 0;
+ /* SOFT BODY */
+
+ virtual RID soft_body_create(bool p_init_sleeping = false) = 0;
+
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) = 0;
+
+ virtual void soft_body_set_space(RID p_body, RID p_space) = 0;
+ virtual RID soft_body_get_space(RID p_body) const = 0;
+
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) = 0;
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) = 0;
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const = 0;
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) = 0;
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const = 0;
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) = 0;
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) = 0;
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) = 0;
+
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) = 0;
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const = 0;
+
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) = 0;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const = 0;
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) = 0;
+ virtual bool soft_body_is_ray_pickable(RID p_body) const = 0;
+
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) = 0;
+ virtual int soft_body_get_simulation_precision(RID p_body) = 0;
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) = 0;
+ virtual real_t soft_body_get_total_mass(RID p_body) = 0;
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_volume_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) = 0;
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) = 0;
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) = 0;
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) = 0;
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) = 0;
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) = 0;
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const = 0;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body) = 0;
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) = 0;
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) = 0;
+
/* JOINT API */
enum JointType {
diff --git a/servers/server_wrap_mt_common.h b/servers/server_wrap_mt_common.h
index 4681dd46f0..611e25af2a 100644
--- a/servers/server_wrap_mt_common.h
+++ b/servers/server_wrap_mt_common.h
@@ -810,3 +810,12 @@
server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); \
} \
}
+
+#define FUNC13(m_type, m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10, m_arg11, m_arg12, m_arg13) \
+ virtual void m_type(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5, m_arg6 p6, m_arg7 p7, m_arg8 p8, m_arg9 p9, m_arg10 p10, m_arg11 p11, m_arg12 p12, m_arg13 p13) { \
+ if (Thread::get_caller_id() != server_thread) { \
+ command_queue.push(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); \
+ } else { \
+ server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); \
+ } \
+ }
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 8d8e9e693e..a8f4377ce7 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -66,7 +66,7 @@ public:
virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) = 0;
virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) = 0;
- virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+ virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
virtual void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0;
@@ -104,10 +104,12 @@ public:
VS::ShadowCastingSetting cast_shadows;
+ //fit in 32 bits
bool mirror : 8;
bool receive_shadows : 8;
bool visible : 8;
- bool baked_light : 8; //this flag is only to know if it actually did use baked light
+ bool baked_light : 4; //this flag is only to know if it actually did use baked light
+ bool redraw_if_visible : 4;
float depth; //used for sorting
@@ -131,6 +133,7 @@ public:
depth_layer = 0;
layer_mask = 1;
baked_light = false;
+ redraw_if_visible = false;
lightmap_capture = NULL;
}
};
@@ -201,6 +204,7 @@ public:
virtual void textures_keep_original(bool p_enable) = 0;
virtual void texture_set_proxy(RID p_proxy, RID p_base) = 0;
+ virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0;
/* SKY API */
@@ -276,25 +280,30 @@ public:
virtual AABB mesh_get_custom_aabb(RID p_mesh) const = 0;
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const = 0;
+
virtual void mesh_clear(RID p_mesh) = 0;
/* MULTIMESH API */
virtual RID multimesh_create() = 0;
- virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format) = 0;
+ virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE) = 0;
virtual int multimesh_get_instance_count(RID p_multimesh) const = 0;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0;
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) = 0;
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0;
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0;
+ virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0;
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;
+ virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0;
+
+ virtual void multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) = 0;
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0;
virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 2069e64c43..fd1eb77143 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -2545,7 +2545,9 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
TkPos pos = _get_tkpos();
tk = _get_token();
- if (tk.type == TK_PERIOD) {
+ if (tk.type == TK_CURSOR) {
+ //do nothing
+ } else if (tk.type == TK_PERIOD) {
StringName identifier;
if (_get_completable_identifier(p_block, COMPLETION_INDEX, identifier)) {
@@ -3583,7 +3585,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
return OK;
}
-Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types) {
+Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) {
Token tk = _get_token();
@@ -3642,7 +3644,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
- if (!p_render_modes.has(mode)) {
+ if (p_render_modes.find(mode) == -1) {
_set_error("Invalid render mode: '" + String(mode) + "'");
return ERR_PARSE_ERROR;
}
@@ -4097,7 +4099,7 @@ String ShaderLanguage::get_shader_type(const String &p_code) {
return String();
}
-Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types) {
+Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) {
clear();
@@ -4114,7 +4116,7 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi
return OK;
}
-Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint) {
+Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint) {
clear();
@@ -4130,13 +4132,13 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
switch (completion_type) {
case COMPLETION_NONE: {
- //do none
- return ERR_PARSE_ERROR;
+ //do nothing
+ return OK;
} break;
case COMPLETION_RENDER_MODE: {
- for (const Set<String>::Element *E = p_render_modes.front(); E; E = E->next()) {
+ for (int i = 0; i < p_render_modes.size(); i++) {
- r_options->push_back(E->get());
+ r_options->push_back(p_render_modes[i]);
}
return OK;
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index 720511e18d..9b84c5f195 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -650,7 +650,7 @@ private:
Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
- Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types);
+ Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types);
public:
//static void get_keyword_list(ShaderType p_type,List<String> *p_keywords);
@@ -658,8 +658,8 @@ public:
void clear();
static String get_shader_type(const String &p_code);
- Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types);
- Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Set<String> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint);
+ Error compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types);
+ Error complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, List<String> *r_options, String &r_call_hint);
String get_error_text();
int get_error_line();
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 95193f7a8f..0de8676f32 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -35,7 +35,7 @@ const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(
return shader_modes[p_mode].functions;
}
-const Set<String> &ShaderTypes::get_modes(VS::ShaderMode p_mode) {
+const Vector<StringName> &ShaderTypes::get_modes(VS::ShaderMode p_mode) {
return shader_modes[p_mode].modes;
}
@@ -140,42 +140,45 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].functions["light"].can_discard = true;
- shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_mix");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_add");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_sub");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("blend_mul");
+ //order used puts first enum mode (default) first
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_mix");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_add");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_sub");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("blend_mul");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_opaque");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_always");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_never");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_draw_alpha_prepass");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_opaque");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_always");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_never");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_draw_alpha_prepass");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("depth_test_disable");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("depth_test_disable");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_front");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_back");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("cull_disabled");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_back");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_front");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("cull_disabled");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("unshaded");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("unshaded");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert_wrap");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_oren_nayar");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_burley");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_toon");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_lambert");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_lambert_wrap");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_oren_nayar");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_burley");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("diffuse_toon");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_schlick_ggx");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_blinn");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_phong");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_toon");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("specular_disabled");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_schlick_ggx");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_blinn");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_phong");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_toon");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("specular_disabled");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("skip_vertex_transform");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("world_vertex_coords");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("skip_vertex_transform");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("world_vertex_coords");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("ensure_correct_normals");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("shadows_disabled");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("shadows_disabled");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("ambient_light_disabled");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("vertex_lighting");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting");
/************ CANVAS ITEM **************************/
@@ -226,17 +229,17 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true;
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("skip_vertex_transform");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("skip_vertex_transform");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_mix");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_add");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_sub");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_mul");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_premul_alpha");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_disabled");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_mix");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_add");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_sub");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_mul");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_premul_alpha");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("blend_disabled");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("unshaded");
- shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("light_only");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("unshaded");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.push_back("light_only");
/************ PARTICLES **************************/
@@ -256,9 +259,9 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].can_discard = false;
- shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");
- shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_velocity");
- shader_modes[VS::SHADER_PARTICLES].modes.insert("keep_data");
+ shader_modes[VS::SHADER_PARTICLES].modes.push_back("disable_force");
+ shader_modes[VS::SHADER_PARTICLES].modes.push_back("disable_velocity");
+ shader_modes[VS::SHADER_PARTICLES].modes.push_back("keep_data");
shader_types.insert("spatial");
shader_types.insert("canvas_item");
diff --git a/servers/visual/shader_types.h b/servers/visual/shader_types.h
index 1f43ff9c92..0680ec8242 100644
--- a/servers/visual/shader_types.h
+++ b/servers/visual/shader_types.h
@@ -31,14 +31,16 @@
#ifndef SHADERTYPES_H
#define SHADERTYPES_H
+#include "ordered_hash_map.h"
#include "servers/visual_server.h"
#include "shader_language.h"
+
class ShaderTypes {
struct Type {
Map<StringName, ShaderLanguage::FunctionInfo> functions;
- Set<String> modes;
+ Vector<StringName> modes;
};
Map<VS::ShaderMode, Type> shader_modes;
@@ -51,7 +53,7 @@ public:
static ShaderTypes *get_singleton() { return singleton; }
const Map<StringName, ShaderLanguage::FunctionInfo> &get_functions(VS::ShaderMode p_mode);
- const Set<String> &get_modes(VS::ShaderMode p_mode);
+ const Vector<StringName> &get_modes(VS::ShaderMode p_mode);
const Set<String> &get_types();
ShaderTypes();
diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp
index fca3126604..5f207b1d3f 100644
--- a/servers/visual/visual_server_raster.cpp
+++ b/servers/visual/visual_server_raster.cpp
@@ -95,6 +95,9 @@ void VisualServerRaster::request_frame_drawn_callback(Object *p_where, const Str
void VisualServerRaster::draw(bool p_swap_buffers) {
+ //needs to be done before changes is reset to 0, to not force the editor to redraw
+ VS::get_singleton()->emit_signal("frame_pre_draw");
+
changes = 0;
VSG::rasterizer->begin_frame();
@@ -122,7 +125,7 @@ void VisualServerRaster::draw(bool p_swap_buffers) {
frame_drawn_callbacks.pop_front();
}
- emit_signal("frame_drawn_in_thread");
+ VS::get_singleton()->emit_signal("frame_post_draw");
}
void VisualServerRaster::sync() {
}
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 8f19de9f8b..ec0d02ed2a 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -139,6 +139,8 @@ public:
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); }
#define BIND12(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12) \
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); }
+#define BIND13(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13) \
+ void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13) { DISPLAY_CHANGED BINDBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); }
//from now on, calls forwarded to this singleton
#define BINDBASE VSG::storage
@@ -171,6 +173,8 @@ public:
BIND2(texture_set_proxy, RID, RID)
+ BIND2(texture_set_force_redraw_if_visible, RID, bool)
+
/* SKY API */
BIND0R(RID, sky_create)
@@ -244,13 +248,14 @@ public:
BIND0R(RID, multimesh_create)
- BIND4(multimesh_allocate, RID, int, MultimeshTransformFormat, MultimeshColorFormat)
+ BIND5(multimesh_allocate, RID, int, MultimeshTransformFormat, MultimeshColorFormat, MultimeshCustomDataFormat)
BIND1RC(int, multimesh_get_instance_count, RID)
BIND2(multimesh_set_mesh, RID, RID)
BIND3(multimesh_instance_set_transform, RID, int, const Transform &)
BIND3(multimesh_instance_set_transform_2d, RID, int, const Transform2D &)
BIND3(multimesh_instance_set_color, RID, int, const Color &)
+ BIND3(multimesh_instance_set_custom_data, RID, int, const Color &)
BIND1RC(RID, multimesh_get_mesh, RID)
BIND1RC(AABB, multimesh_get_aabb, RID)
@@ -258,6 +263,9 @@ public:
BIND2RC(Transform, multimesh_instance_get_transform, RID, int)
BIND2RC(Transform2D, multimesh_instance_get_transform_2d, RID, int)
BIND2RC(Color, multimesh_instance_get_color, RID, int)
+ BIND2RC(Color, multimesh_instance_get_custom_data, RID, int)
+
+ BIND2(multimesh_set_as_bulk_array, RID, const PoolVector<float> &)
BIND2(multimesh_set_visible_instances, RID, int)
BIND1RC(int, multimesh_get_visible_instances, RID)
@@ -489,7 +497,7 @@ public:
BIND2(environment_set_canvas_max_layer, RID, int)
BIND4(environment_set_ambient_light, RID, const Color &, float, float)
BIND7(environment_set_ssr, RID, bool, int, float, float, float, bool)
- BIND12(environment_set_ssao, RID, bool, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
+ BIND13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
BIND6(environment_set_dof_blur_near, RID, bool, float, float, float, EnvironmentDOFBlurQuality)
BIND6(environment_set_dof_blur_far, RID, bool, float, float, float, EnvironmentDOFBlurQuality)
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index 43a225b370..887cd7429a 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -820,6 +820,11 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF
instance->baked_light = p_enabled;
} break;
+ case VS::INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE: {
+
+ instance->redraw_if_visible = p_enabled;
+
+ } break;
}
}
void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) {
@@ -1257,6 +1262,9 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data);
+ Transform light_transform = p_instance->transform;
+ light_transform.orthonormalize(); //scale does not count on lights
+
switch (VSG::storage->light_get_type(p_instance->base)) {
case VS::LIGHT_DIRECTIONAL: {
@@ -1359,7 +1367,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
// obtain the light frustm ranges (given endpoints)
- Transform transform = p_instance->transform.orthonormalized(); //discard scale and stabilize light
+ Transform transform = light_transform; //discard scale and stabilize light
Vector3 x_vec = transform.basis.get_axis(Vector3::AXIS_X).normalized();
Vector3 y_vec = transform.basis.get_axis(Vector3::AXIS_Y).normalized();
@@ -1469,7 +1477,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
// a pre pass will need to be needed to determine the actual z-near to be used
- Plane near_plane(p_instance->transform.origin, -p_instance->transform.basis.get_axis(2));
+ Plane near_plane(light_transform.origin, -light_transform.basis.get_axis(2));
for (int j = 0; j < cull_count; j++) {
@@ -1524,14 +1532,14 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
float z = i == 0 ? -1 : 1;
Vector<Plane> planes;
planes.resize(5);
- planes[0] = p_instance->transform.xform(Plane(Vector3(0, 0, z), radius));
- planes[1] = p_instance->transform.xform(Plane(Vector3(1, 0, z).normalized(), radius));
- planes[2] = p_instance->transform.xform(Plane(Vector3(-1, 0, z).normalized(), radius));
- planes[3] = p_instance->transform.xform(Plane(Vector3(0, 1, z).normalized(), radius));
- planes[4] = p_instance->transform.xform(Plane(Vector3(0, -1, z).normalized(), radius));
+ planes[0] = light_transform.xform(Plane(Vector3(0, 0, z), radius));
+ planes[1] = light_transform.xform(Plane(Vector3(1, 0, z).normalized(), radius));
+ planes[2] = light_transform.xform(Plane(Vector3(-1, 0, z).normalized(), radius));
+ planes[3] = light_transform.xform(Plane(Vector3(0, 1, z).normalized(), radius));
+ planes[4] = light_transform.xform(Plane(Vector3(0, -1, z).normalized(), radius));
int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK);
- Plane near_plane(p_instance->transform.origin, p_instance->transform.basis.get_axis(2) * z);
+ Plane near_plane(light_transform.origin, light_transform.basis.get_axis(2) * z);
for (int j = 0; j < cull_count; j++) {
@@ -1546,7 +1554,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
}
}
- VSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), p_instance->transform, radius, 0, i);
+ VSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), light_transform, radius, 0, i);
VSG::scene_render->render_shadow(light->instance, p_shadow_atlas, i, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count);
}
} break;
@@ -1577,7 +1585,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
Vector3(0, -1, 0)
};
- Transform xform = p_instance->transform * Transform().looking_at(view_normals[i], view_up[i]);
+ Transform xform = light_transform * Transform().looking_at(view_normals[i], view_up[i]);
Vector<Plane> planes = cm.get_projection_planes(xform);
@@ -1602,7 +1610,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
}
//restore the regular DP matrix
- VSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), p_instance->transform, radius, 0, 0);
+ VSG::scene_render->light_instance_set_shadow_transform(light->instance, CameraMatrix(), light_transform, radius, 0, 0);
} break;
}
@@ -1616,10 +1624,10 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
CameraMatrix cm;
cm.set_perspective(angle * 2.0, 1.0, 0.01, radius);
- Vector<Plane> planes = cm.get_projection_planes(p_instance->transform);
+ Vector<Plane> planes = cm.get_projection_planes(light_transform);
int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK);
- Plane near_plane(p_instance->transform.origin, -p_instance->transform.basis.get_axis(2));
+ Plane near_plane(light_transform.origin, -light_transform.basis.get_axis(2));
for (int j = 0; j < cull_count; j++) {
Instance *instance = instance_shadow_cull_result[j];
@@ -1633,7 +1641,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
}
}
- VSG::scene_render->light_instance_set_shadow_transform(light->instance, cm, p_instance->transform, radius, 0, 0);
+ VSG::scene_render->light_instance_set_shadow_transform(light->instance, cm, light_transform, radius, 0, 0);
VSG::scene_render->render_shadow(light->instance, p_shadow_atlas, 0, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, cull_count);
} break;
@@ -1641,7 +1649,8 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
}
void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) {
- // render to mono camera
+// render to mono camera
+#ifndef _3D_DISABLED
Camera *camera = camera_owner.getornull(p_camera);
ERR_FAIL_COND(!camera);
@@ -1676,6 +1685,7 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view
_prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
_render_scene(camera->transform, camera_matrix, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
+#endif
}
void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) {
@@ -1715,7 +1725,7 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
float width = (2.0 * z_near) / camera_matrix.matrix[0][0];
float x_shift = width * camera_matrix.matrix[2][0];
float height = (2.0 * z_near) / camera_matrix.matrix[1][1];
- float y_shift = width * camera_matrix.matrix[2][1];
+ float y_shift = height * camera_matrix.matrix[2][1];
// printf("Eye_dist = %f, Near = %f, Far = %f, Width = %f, Shift = %f\n", eye_dist, z_near, z_far, width, x_shift);
@@ -1735,10 +1745,10 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
float z_shift = (left_near / slope) - z_near;
// - figure out new vertical near plane size (this will be slightly oversized thanks to our z-shift)
- float top_near = (height + y_shift) * 0.5;
- top_near += y_shift * z_shift;
- float bottom_near = -(height - y_shift) * 0.5;
- bottom_near -= y_shift * z_shift;
+ float top_near = (height - y_shift) * 0.5;
+ top_near += (top_near / z_near) * z_shift;
+ float bottom_near = -(height + y_shift) * 0.5;
+ bottom_near += (bottom_near / z_near) * z_shift;
// printf("Left_near = %f, Left_far = %f, Top_near = %f, Bottom_near = %f, Z_shift = %f\n", left_near, left_far, top_near, bottom_near, z_shift);
@@ -1868,6 +1878,10 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(ins->base_data);
+ if (ins->redraw_if_visible) {
+ VisualServerRaster::redraw_request();
+ }
+
if (ins->base_type == VS::INSTANCE_PARTICLES) {
//particles visible? process them
VSG::storage->particles_request_process(ins->base);
@@ -2099,6 +2113,8 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
void VisualServerScene::render_empty_scene(RID p_scenario, RID p_shadow_atlas) {
+#ifndef _3D_DISABLED
+
Scenario *scenario = scenario_owner.getornull(p_scenario);
RID environment;
@@ -2107,6 +2123,7 @@ void VisualServerScene::render_empty_scene(RID p_scenario, RID p_shadow_atlas) {
else
environment = scenario->fallback_environment;
VSG::scene_render->render_scene(Transform(), CameraMatrix(), true, NULL, 0, NULL, 0, NULL, 0, environment, p_shadow_atlas, scenario->reflection_atlas, RID(), 0);
+#endif
}
bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int p_step) {
diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp
index dcc270ca5e..dd6bc3cf26 100644
--- a/servers/visual/visual_server_viewport.cpp
+++ b/servers/visual/visual_server_viewport.cpp
@@ -268,7 +268,7 @@ void VisualServerViewport::draw_viewports() {
ERR_CONTINUE(!vp->render_target.is_valid());
bool visible = vp->viewport_to_screen_rect != Rect2() || vp->update_mode == VS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == VS::VIEWPORT_UPDATE_ONCE || (vp->update_mode == VS::VIEWPORT_UPDATE_WHEN_VISIBLE && VSG::storage->render_target_was_used(vp->render_target));
- visible = visible && vp->size.x > 0 && vp->size.y > 0;
+ visible = visible && vp->size.x > 1 && vp->size.y > 1;
if (!visible)
continue;
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index 19bb58f3ad..48f0ec46f3 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -107,6 +107,8 @@ public:
FUNC2(texture_set_proxy, RID, RID)
+ FUNC2(texture_set_force_redraw_if_visible, RID, bool)
+
/* SKY API */
FUNCRID(sky)
@@ -180,13 +182,14 @@ public:
FUNCRID(multimesh)
- FUNC4(multimesh_allocate, RID, int, MultimeshTransformFormat, MultimeshColorFormat)
+ FUNC5(multimesh_allocate, RID, int, MultimeshTransformFormat, MultimeshColorFormat, MultimeshCustomDataFormat)
FUNC1RC(int, multimesh_get_instance_count, RID)
FUNC2(multimesh_set_mesh, RID, RID)
FUNC3(multimesh_instance_set_transform, RID, int, const Transform &)
FUNC3(multimesh_instance_set_transform_2d, RID, int, const Transform2D &)
FUNC3(multimesh_instance_set_color, RID, int, const Color &)
+ FUNC3(multimesh_instance_set_custom_data, RID, int, const Color &)
FUNC1RC(RID, multimesh_get_mesh, RID)
FUNC1RC(AABB, multimesh_get_aabb, RID)
@@ -194,6 +197,9 @@ public:
FUNC2RC(Transform, multimesh_instance_get_transform, RID, int)
FUNC2RC(Transform2D, multimesh_instance_get_transform_2d, RID, int)
FUNC2RC(Color, multimesh_instance_get_color, RID, int)
+ FUNC2RC(Color, multimesh_instance_get_custom_data, RID, int)
+
+ FUNC2(multimesh_set_as_bulk_array, RID, const PoolVector<float> &)
FUNC2(multimesh_set_visible_instances, RID, int)
FUNC1RC(int, multimesh_get_visible_instances, RID)
@@ -416,7 +422,7 @@ public:
FUNC2(environment_set_canvas_max_layer, RID, int)
FUNC4(environment_set_ambient_light, RID, const Color &, float, float)
FUNC7(environment_set_ssr, RID, bool, int, float, float, float, bool)
- FUNC12(environment_set_ssao, RID, bool, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
+ FUNC13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
FUNC6(environment_set_dof_blur_near, RID, bool, float, float, float, EnvironmentDOFBlurQuality)
FUNC6(environment_set_dof_blur_far, RID, bool, float, float, float, EnvironmentDOFBlurQuality)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 21745e87a8..8e21341cee 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -795,6 +795,140 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_
return OK;
}
+uint32_t VisualServer::mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const {
+ uint32_t offsets[ARRAY_MAX];
+ mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets);
+ return offsets[p_array_index];
+}
+
+uint32_t VisualServer::mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const {
+ uint32_t offsets[ARRAY_MAX];
+ return mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets);
+}
+
+uint32_t VisualServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const {
+
+ int total_elem_size = 0;
+
+ for (int i = 0; i < VS::ARRAY_MAX; i++) {
+
+ r_offsets[i] = 0; //reset
+
+ if (!(p_format & (1 << i))) // no array
+ continue;
+
+ int elem_size = 0;
+
+ switch (i) {
+
+ case VS::ARRAY_VERTEX: {
+
+ if (p_format & ARRAY_FLAG_USE_2D_VERTICES) {
+ elem_size = 2;
+ } else {
+ elem_size = 3;
+ }
+
+ if (p_format & ARRAY_COMPRESS_VERTEX) {
+ elem_size *= sizeof(int16_t);
+ } else {
+ elem_size *= sizeof(float);
+ }
+
+ if (elem_size == 6) {
+ elem_size = 8;
+ }
+
+ } break;
+ case VS::ARRAY_NORMAL: {
+
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 3;
+ }
+
+ } break;
+
+ case VS::ARRAY_TANGENT: {
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+
+ } break;
+ case VS::ARRAY_COLOR: {
+
+ if (p_format & ARRAY_COMPRESS_COLOR) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+ } break;
+ case VS::ARRAY_TEX_UV: {
+ if (p_format & ARRAY_COMPRESS_TEX_UV) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 2;
+ }
+
+ } break;
+
+ case VS::ARRAY_TEX_UV2: {
+ if (p_format & ARRAY_COMPRESS_TEX_UV2) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 2;
+ }
+
+ } break;
+ case VS::ARRAY_WEIGHTS: {
+
+ if (p_format & ARRAY_COMPRESS_WEIGHTS) {
+ elem_size = sizeof(uint16_t) * 4;
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+
+ } break;
+ case VS::ARRAY_BONES: {
+
+ if (p_format & ARRAY_FLAG_USE_16_BIT_BONES) {
+ elem_size = sizeof(uint16_t) * 4;
+ } else {
+ elem_size = sizeof(uint32_t);
+ }
+
+ } break;
+ case VS::ARRAY_INDEX: {
+
+ if (p_index_len <= 0) {
+ ERR_PRINT("index_array_len==NO_INDEX_ARRAY");
+ break;
+ }
+ /* determine whether using 16 or 32 bits indices */
+ if (p_vertex_len >= (1 << 16)) {
+
+ elem_size = 4;
+
+ } else {
+ elem_size = 2;
+ }
+ r_offsets[i] = elem_size;
+ continue;
+ } break;
+ default: {
+ ERR_FAIL_V(0);
+ }
+ }
+
+ r_offsets[i] = total_elem_size;
+ total_elem_size += elem_size;
+ }
+ return total_elem_size;
+}
+
void VisualServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) {
ERR_FAIL_INDEX(p_primitive, VS::PRIMITIVE_MAX);
@@ -1543,10 +1677,10 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_debug_usage"), &VisualServer::_texture_debug_usage_bind);
ClassDB::bind_method(D_METHOD("textures_keep_original", "enable"), &VisualServer::textures_keep_original);
-
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("sky_create"), &VisualServer::sky_create);
ClassDB::bind_method(D_METHOD("sky_set_texture", "sky", "cube_map", "radiance_size"), &VisualServer::sky_set_texture);
-
+#endif
ClassDB::bind_method(D_METHOD("shader_create"), &VisualServer::shader_create);
ClassDB::bind_method(D_METHOD("shader_set_code", "shader", "code"), &VisualServer::shader_set_code);
ClassDB::bind_method(D_METHOD("shader_get_code", "shader"), &VisualServer::shader_get_code);
@@ -1564,11 +1698,14 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("material_set_next_pass", "material", "next_material"), &VisualServer::material_set_next_pass);
ClassDB::bind_method(D_METHOD("mesh_create"), &VisualServer::mesh_create);
+ ClassDB::bind_method(D_METHOD("mesh_surface_get_format_offset", "format", "vertex_len", "index_len", "array_index"), &VisualServer::mesh_surface_get_format_offset);
+ ClassDB::bind_method(D_METHOD("mesh_surface_get_format_stride", "format", "vertex_len", "index_len"), &VisualServer::mesh_surface_get_format_stride);
ClassDB::bind_method(D_METHOD("mesh_add_surface_from_arrays", "mesh", "primtive", "arrays", "blend_shapes", "compress_format"), &VisualServer::mesh_add_surface_from_arrays, DEFVAL(Array()), DEFVAL(ARRAY_COMPRESS_DEFAULT));
ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_count", "mesh", "amount"), &VisualServer::mesh_set_blend_shape_count);
ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_count", "mesh"), &VisualServer::mesh_get_blend_shape_count);
ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_mode", "mesh", "mode"), &VisualServer::mesh_set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_mode", "mesh"), &VisualServer::mesh_get_blend_shape_mode);
+ ClassDB::bind_method(D_METHOD("mesh_surface_update_region", "mesh", "surface", "offset", "data"), &VisualServer::mesh_surface_update_region);
ClassDB::bind_method(D_METHOD("mesh_surface_set_material", "mesh", "surface", "material"), &VisualServer::mesh_surface_set_material);
ClassDB::bind_method(D_METHOD("mesh_surface_get_material", "mesh", "surface"), &VisualServer::mesh_surface_get_material);
ClassDB::bind_method(D_METHOD("mesh_surface_get_array_len", "mesh", "surface"), &VisualServer::mesh_surface_get_array_len);
@@ -1587,20 +1724,23 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("mesh_get_custom_aabb", "mesh"), &VisualServer::mesh_get_custom_aabb);
ClassDB::bind_method(D_METHOD("mesh_clear", "mesh"), &VisualServer::mesh_clear);
- ClassDB::bind_method(D_METHOD("multimesh_allocate", "multimesh", "instances", "transform_format", "color_format"), &VisualServer::multimesh_allocate);
+ ClassDB::bind_method(D_METHOD("multimesh_allocate", "multimesh", "instances", "transform_format", "color_format", "custom_data_format"), &VisualServer::multimesh_allocate, DEFVAL(MULTIMESH_CUSTOM_DATA_NONE));
ClassDB::bind_method(D_METHOD("multimesh_get_instance_count", "multimesh"), &VisualServer::multimesh_get_instance_count);
ClassDB::bind_method(D_METHOD("multimesh_set_mesh", "multimesh", "mesh"), &VisualServer::multimesh_set_mesh);
ClassDB::bind_method(D_METHOD("multimesh_instance_set_transform", "multimesh", "index", "transform"), &VisualServer::multimesh_instance_set_transform);
ClassDB::bind_method(D_METHOD("multimesh_instance_set_transform_2d", "multimesh", "index", "transform"), &VisualServer::multimesh_instance_set_transform_2d);
ClassDB::bind_method(D_METHOD("multimesh_instance_set_color", "multimesh", "index", "color"), &VisualServer::multimesh_instance_set_color);
+ ClassDB::bind_method(D_METHOD("multimesh_instance_set_custom_data", "multimesh", "index", "custom_data"), &VisualServer::multimesh_instance_set_custom_data);
ClassDB::bind_method(D_METHOD("multimesh_get_mesh", "multimesh"), &VisualServer::multimesh_get_mesh);
ClassDB::bind_method(D_METHOD("multimesh_get_aabb", "multimesh"), &VisualServer::multimesh_get_aabb);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform", "multimesh", "index"), &VisualServer::multimesh_instance_get_transform);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform_2d", "multimesh", "index"), &VisualServer::multimesh_instance_get_transform_2d);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_color", "multimesh", "index"), &VisualServer::multimesh_instance_get_color);
+ ClassDB::bind_method(D_METHOD("multimesh_instance_get_custom_data", "multimesh", "index"), &VisualServer::multimesh_instance_get_custom_data);
ClassDB::bind_method(D_METHOD("multimesh_set_visible_instances", "multimesh", "visible"), &VisualServer::multimesh_set_visible_instances);
ClassDB::bind_method(D_METHOD("multimesh_get_visible_instances", "multimesh"), &VisualServer::multimesh_get_visible_instances);
-
+ ClassDB::bind_method(D_METHOD("multimesh_set_as_bulk_array", "multimesh", "array"), &VisualServer::multimesh_set_as_bulk_array);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("immediate_create"), &VisualServer::immediate_create);
ClassDB::bind_method(D_METHOD("immediate_begin", "immediate", "primitive", "texture"), &VisualServer::immediate_begin, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("immediate_vertex", "immediate", "vertex"), &VisualServer::immediate_vertex);
@@ -1614,6 +1754,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("immediate_clear", "immediate"), &VisualServer::immediate_clear);
ClassDB::bind_method(D_METHOD("immediate_set_material", "immediate", "material"), &VisualServer::immediate_set_material);
ClassDB::bind_method(D_METHOD("immediate_get_material", "immediate"), &VisualServer::immediate_get_material);
+#endif
ClassDB::bind_method(D_METHOD("skeleton_create"), &VisualServer::skeleton_create);
ClassDB::bind_method(D_METHOD("skeleton_allocate", "skeleton", "bones", "is_2d_skeleton"), &VisualServer::skeleton_allocate, DEFVAL(false));
@@ -1623,6 +1764,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("skeleton_bone_set_transform_2d", "skeleton", "bone", "transform"), &VisualServer::skeleton_bone_set_transform_2d);
ClassDB::bind_method(D_METHOD("skeleton_bone_get_transform_2d", "skeleton", "bone"), &VisualServer::skeleton_bone_get_transform_2d);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("directional_light_create"), &VisualServer::directional_light_create);
ClassDB::bind_method(D_METHOD("omni_light_create"), &VisualServer::omni_light_create);
ClassDB::bind_method(D_METHOD("spot_light_create"), &VisualServer::spot_light_create);
@@ -1692,7 +1834,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("lightmap_capture_get_octree", "capture"), &VisualServer::lightmap_capture_get_octree);
ClassDB::bind_method(D_METHOD("lightmap_capture_set_energy", "capture", "energy"), &VisualServer::lightmap_capture_set_energy);
ClassDB::bind_method(D_METHOD("lightmap_capture_get_energy", "capture"), &VisualServer::lightmap_capture_get_energy);
-
+#endif
ClassDB::bind_method(D_METHOD("particles_create"), &VisualServer::particles_create);
ClassDB::bind_method(D_METHOD("particles_set_emitting", "particles", "emitting"), &VisualServer::particles_set_emitting);
ClassDB::bind_method(D_METHOD("particles_get_emitting", "particles"), &VisualServer::particles_get_emitting);
@@ -1779,6 +1921,8 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("scenario_set_reflection_atlas_size", "scenario", "p_size", "subdiv"), &VisualServer::scenario_set_reflection_atlas_size);
ClassDB::bind_method(D_METHOD("scenario_set_fallback_environment", "scenario", "environment"), &VisualServer::scenario_set_fallback_environment);
+#ifndef _3D_DISABLED
+
ClassDB::bind_method(D_METHOD("instance_create2", "base", "scenario"), &VisualServer::instance_create2);
ClassDB::bind_method(D_METHOD("instance_create"), &VisualServer::instance_create);
ClassDB::bind_method(D_METHOD("instance_set_base", "instance", "base"), &VisualServer::instance_set_base);
@@ -1803,7 +1947,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &VisualServer::_instances_cull_aabb_bind, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &VisualServer::_instances_cull_ray_bind, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &VisualServer::_instances_cull_convex_bind, DEFVAL(RID()));
-
+#endif
ClassDB::bind_method(D_METHOD("canvas_create"), &VisualServer::canvas_create);
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &VisualServer::canvas_set_item_mirroring);
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &VisualServer::canvas_set_modulate);
@@ -1886,13 +2030,14 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("init"), &VisualServer::init);
ClassDB::bind_method(D_METHOD("finish"), &VisualServer::finish);
ClassDB::bind_method(D_METHOD("get_render_info", "info"), &VisualServer::get_render_info);
+#ifndef _3D_DISABLED
+ ClassDB::bind_method(D_METHOD("make_sphere_mesh", "latitudes", "longitudes", "radius"), &VisualServer::make_sphere_mesh);
ClassDB::bind_method(D_METHOD("get_test_cube"), &VisualServer::get_test_cube);
+#endif
ClassDB::bind_method(D_METHOD("get_test_texture"), &VisualServer::get_test_texture);
ClassDB::bind_method(D_METHOD("get_white_texture"), &VisualServer::get_white_texture);
- ClassDB::bind_method(D_METHOD("make_sphere_mesh", "latitudes", "longitudes", "radius"), &VisualServer::make_sphere_mesh);
-
ClassDB::bind_method(D_METHOD("set_boot_image", "image", "color", "scale"), &VisualServer::set_boot_image);
ClassDB::bind_method(D_METHOD("set_default_clear_color", "color"), &VisualServer::set_default_clear_color);
@@ -2058,6 +2203,7 @@ void VisualServer::_bind_methods() {
BIND_ENUM_CONSTANT(INSTANCE_GEOMETRY_MASK);
BIND_ENUM_CONSTANT(INSTANCE_FLAG_USE_BAKED_LIGHT);
+ BIND_ENUM_CONSTANT(INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE);
BIND_ENUM_CONSTANT(INSTANCE_FLAG_MAX);
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF);
@@ -2143,7 +2289,8 @@ void VisualServer::_bind_methods() {
BIND_ENUM_CONSTANT(ENV_SSAO_BLUR_2x2);
BIND_ENUM_CONSTANT(ENV_SSAO_BLUR_3x3);
- ADD_SIGNAL(MethodInfo("frame_drawn_in_thread"));
+ ADD_SIGNAL(MethodInfo("frame_pre_draw"));
+ ADD_SIGNAL(MethodInfo("frame_post_draw"));
}
void VisualServer::_canvas_item_add_style_box(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector<float> &p_margins, const Color &p_modulate) {
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 65d0f07a43..367642b7d4 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -144,6 +144,7 @@ public:
virtual void textures_keep_original(bool p_enable) = 0;
virtual void texture_set_proxy(RID p_proxy, RID p_base) = 0;
+ virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0;
/* SKY API */
@@ -233,7 +234,7 @@ public:
ARRAY_FLAG_USE_16_BIT_BONES = ARRAY_COMPRESS_INDEX << 2,
ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_VERTEX | ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
+ ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
};
@@ -250,6 +251,10 @@ public:
virtual RID mesh_create() = 0;
+ virtual uint32_t mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const;
+ virtual uint32_t mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const;
+ /// Returns stride
+ virtual uint32_t mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const;
virtual void mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), uint32_t p_compress_format = ARRAY_COMPRESS_DEFAULT);
virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>()) = 0;
@@ -309,13 +314,20 @@ public:
MULTIMESH_COLOR_FLOAT,
};
- virtual void multimesh_allocate(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, MultimeshColorFormat p_color_format) = 0;
+ enum MultimeshCustomDataFormat {
+ MULTIMESH_CUSTOM_DATA_NONE,
+ MULTIMESH_CUSTOM_DATA_8BIT,
+ MULTIMESH_CUSTOM_DATA_FLOAT,
+ };
+
+ virtual void multimesh_allocate(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, MultimeshColorFormat p_color_format, MultimeshCustomDataFormat p_data_format = MULTIMESH_CUSTOM_DATA_NONE) = 0;
virtual int multimesh_get_instance_count(RID p_multimesh) const = 0;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0;
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) = 0;
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0;
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0;
+ virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0;
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0;
@@ -323,6 +335,9 @@ public:
virtual Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;
+ virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0;
+
+ virtual void multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) = 0;
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0;
virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0;
@@ -719,7 +734,7 @@ public:
ENV_SSAO_BLUR_3x3,
};
- virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, const Color &p_color, EnvironmentSSAOQuality p_quality, EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+ virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, EnvironmentSSAOQuality p_quality, EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0;
virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
@@ -794,6 +809,7 @@ public:
enum InstanceFlags {
INSTANCE_FLAG_USE_BAKED_LIGHT,
+ INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE,
INSTANCE_FLAG_MAX
};
@@ -1023,6 +1039,7 @@ VARIANT_ENUM_CAST(VisualServer::RenderInfo);
VARIANT_ENUM_CAST(VisualServer::Features);
VARIANT_ENUM_CAST(VisualServer::MultimeshTransformFormat);
VARIANT_ENUM_CAST(VisualServer::MultimeshColorFormat);
+VARIANT_ENUM_CAST(VisualServer::MultimeshCustomDataFormat);
VARIANT_ENUM_CAST(VisualServer::LightOmniShadowMode);
VARIANT_ENUM_CAST(VisualServer::LightOmniShadowDetail);
VARIANT_ENUM_CAST(VisualServer::LightDirectionalShadowMode);
diff --git a/thirdparty/README.md b/thirdparty/README.md
index d30b70fede..ea2a996812 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -402,15 +402,18 @@ Files extracted from upstream source:
## pcre2
- Upstream: http://www.pcre.org/
-- Version: 10.23
+- Version: 10.31
- License: BSD-3-Clause
Files extracted from upstream source:
-- Files listed in NON-AUTOTOOLS-BUILD steps 1-4
+- Files listed in the file NON-AUTOTOOLS-BUILD steps 1-4
- All .h files in src/
-- src/pcre2_jit_*.c and src/sljit/*
-- AUTHORS and COPYING
+- src/pcre2_jit_match.c
+- src/pcre2_jit_misc.c
+- src/pcre2_jit_maketables.c
+- src/sljit/*
+- AUTHORS and LICENCE
## pvrtccompressor
diff --git a/thirdparty/mbedtls/1453.diff b/thirdparty/mbedtls/1453.diff
index acc3654cd4..fef60fc0e6 100644
--- a/thirdparty/mbedtls/1453.diff
+++ b/thirdparty/mbedtls/1453.diff
@@ -1,7 +1,7 @@
-diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
-index 67900c46c8..cefe882d2a 100644
---- a/thirdparty/mbedtls/library/entropy_poll.c
-+++ b/thirdparty/mbedtls/library/entropy_poll.c
+diff --git a/library/entropy_poll.c b/library/entropy_poll.c
+index 67900c46..cefe882d 100644
+--- a/library/entropy_poll.c
++++ b/library/entropy_poll.c
@@ -54,28 +54,43 @@
#define _WIN32_WINNT 0x0400
#endif
@@ -53,11 +53,11 @@ index 67900c46c8..cefe882d2a 100644
*olen = len;
return( 0 );
-diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
-index afff4e18bf..7960fa1a1a 100644
---- a/thirdparty/mbedtls/library/x509_crt.c
-+++ b/thirdparty/mbedtls/library/x509_crt.c
-@@ -64,6 +64,19 @@
+diff --git a/library/x509_crt.c b/library/x509_crt.c
+index 290c1eb3..038eae02 100644
+--- a/library/x509_crt.c
++++ b/library/x509_crt.c
+@@ -65,6 +65,19 @@
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
#include <windows.h>
@@ -77,7 +77,7 @@ index afff4e18bf..7960fa1a1a 100644
#else
#include <time.h>
#endif
-@@ -1130,6 +1143,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -1126,6 +1139,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
char filename[MAX_PATH];
char *p;
size_t len = strlen( path );
@@ -85,7 +85,7 @@ index afff4e18bf..7960fa1a1a 100644
WIN32_FIND_DATAW file_data;
HANDLE hFind;
-@@ -1144,7 +1158,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -1140,7 +1154,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
p = filename + len;
filename[len++] = '*';
@@ -105,7 +105,7 @@ index afff4e18bf..7960fa1a1a 100644
MAX_PATH - 3 );
if( w_ret == 0 )
return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
-@@ -1161,8 +1186,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -1157,8 +1182,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
continue;
diff --git a/thirdparty/mbedtls/include/mbedtls/aes.h b/thirdparty/mbedtls/include/mbedtls/aes.h
index 46016dcb7f..dd5c1183a5 100644
--- a/thirdparty/mbedtls/include/mbedtls/aes.h
+++ b/thirdparty/mbedtls/include/mbedtls/aes.h
@@ -1,7 +1,9 @@
/**
* \file aes.h
*
- * \brief The Advanced Encryption Standard (AES) specifies a FIPS-approved
+ * \brief This file contains AES definitions and functions.
+ *
+ * The Advanced Encryption Standard (AES) specifies a FIPS-approved
* cryptographic algorithm that can be used to protect electronic
* data.
*
@@ -12,6 +14,7 @@
* techniques -- Encryption algorithms -- Part 2: Asymmetric
* ciphers</em>.
*/
+
/* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*
@@ -50,7 +53,8 @@
#define MBEDTLS_ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */
#define MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. */
-/* Error codes in range 0x0023-0x0025 */
+/* Error codes in range 0x0021-0x0025 */
+#define MBEDTLS_ERR_AES_BAD_INPUT_DATA -0x0021 /**< Invalid input data. */
#define MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE -0x0023 /**< Feature not available. For example, an unsupported AES key size. */
#define MBEDTLS_ERR_AES_HW_ACCEL_FAILED -0x0025 /**< AES hardware accelerator failed. */
@@ -59,14 +63,14 @@
#define inline __inline
#endif
-#if !defined(MBEDTLS_AES_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_AES_ALT)
+// Regular implementation
+//
+
/**
* \brief The AES context-type definition.
*/
@@ -85,6 +89,10 @@ typedef struct
}
mbedtls_aes_context;
+#else /* MBEDTLS_AES_ALT */
+#include "aes_alt.h"
+#endif /* MBEDTLS_AES_ALT */
+
/**
* \brief This function initializes the specified AES context.
*
@@ -112,8 +120,8 @@ void mbedtls_aes_free( mbedtls_aes_context *ctx );
* <li>192 bits</li>
* <li>256 bits</li></ul>
*
- * \return \c 0 on success or #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH
- * on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
*/
int mbedtls_aes_setkey_enc( mbedtls_aes_context *ctx, const unsigned char *key,
unsigned int keybits );
@@ -128,7 +136,8 @@ int mbedtls_aes_setkey_enc( mbedtls_aes_context *ctx, const unsigned char *key,
* <li>192 bits</li>
* <li>256 bits</li></ul>
*
- * \return \c 0 on success, or #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
*/
int mbedtls_aes_setkey_dec( mbedtls_aes_context *ctx, const unsigned char *key,
unsigned int keybits );
@@ -192,7 +201,8 @@ int mbedtls_aes_crypt_ecb( mbedtls_aes_context *ctx,
* \param input The buffer holding the input data.
* \param output The buffer holding the output data.
*
- * \return \c 0 on success, or #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH
* on failure.
*/
int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx,
@@ -300,7 +310,49 @@ int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx,
* must use the context initialized with mbedtls_aes_setkey_enc()
* for both #MBEDTLS_AES_ENCRYPT and #MBEDTLS_AES_DECRYPT.
*
- * \warning You must keep the maximum use of your counter in mind.
+ * \warning You must never reuse a nonce value with the same key. Doing so
+ * would void the encryption for the two messages encrypted with
+ * the same nonce and key.
+ *
+ * There are two common strategies for managing nonces with CTR:
+ *
+ * 1. You can handle everything as a single message processed over
+ * successive calls to this function. In that case, you want to
+ * set \p nonce_counter and \p nc_off to 0 for the first call, and
+ * then preserve the values of \p nonce_counter, \p nc_off and \p
+ * stream_block across calls to this function as they will be
+ * updated by this function.
+ *
+ * With this strategy, you must not encrypt more than 2**128
+ * blocks of data with the same key.
+ *
+ * 2. You can encrypt separate messages by dividing the \p
+ * nonce_counter buffer in two areas: the first one used for a
+ * per-message nonce, handled by yourself, and the second one
+ * updated by this function internally.
+ *
+ * For example, you might reserve the first 12 bytes for the
+ * per-message nonce, and the last 4 bytes for internal use. In that
+ * case, before calling this function on a new message you need to
+ * set the first 12 bytes of \p nonce_counter to your chosen nonce
+ * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p
+ * stream_block to be ignored). That way, you can encrypt at most
+ * 2**96 messages of up to 2**32 blocks each with the same key.
+ *
+ * The per-message nonce (or information sufficient to reconstruct
+ * it) needs to be communicated with the ciphertext and must be unique.
+ * The recommended way to ensure uniqueness is to use a message
+ * counter. An alternative is to generate random nonces, but this
+ * limits the number of messages that can be securely encrypted:
+ * for example, with 96-bit random nonces, you should not encrypt
+ * more than 2**32 messages with the same key.
+ *
+ * Note that for both stategies, sizes are measured in blocks and
+ * that an AES block is 16 bytes.
+ *
+ * \warning Upon return, \p stream_block contains sensitive data. Its
+ * content must not be written to insecure storage and should be
+ * securely discarded as soon as it's no longer needed.
*
* \param ctx The AES context to use for encryption or decryption.
* \param length The length of the input data.
@@ -313,7 +365,7 @@ int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx,
* \param input The buffer holding the input data.
* \param output The buffer holding the output data.
*
- * \return \c 0 on success.
+ * \return \c 0 on success.
*/
int mbedtls_aes_crypt_ctr( mbedtls_aes_context *ctx,
size_t length,
@@ -391,22 +443,11 @@ MBEDTLS_DEPRECATED void mbedtls_aes_decrypt( mbedtls_aes_context *ctx,
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_AES_ALT */
-#include "aes_alt.h"
-#endif /* MBEDTLS_AES_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_aes_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/arc4.h b/thirdparty/mbedtls/include/mbedtls/arc4.h
index f9d93f822f..f11fc5be0a 100644
--- a/thirdparty/mbedtls/include/mbedtls/arc4.h
+++ b/thirdparty/mbedtls/include/mbedtls/arc4.h
@@ -38,14 +38,14 @@
#define MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED -0x0019 /**< ARC4 hardware accelerator failed. */
-#if !defined(MBEDTLS_ARC4_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_ARC4_ALT)
+// Regular implementation
+//
+
/**
* \brief ARC4 context structure
*
@@ -61,6 +61,10 @@ typedef struct
}
mbedtls_arc4_context;
+#else /* MBEDTLS_ARC4_ALT */
+#include "arc4_alt.h"
+#endif /* MBEDTLS_ARC4_ALT */
+
/**
* \brief Initialize ARC4 context
*
@@ -118,18 +122,6 @@ void mbedtls_arc4_setup( mbedtls_arc4_context *ctx, const unsigned char *key,
int mbedtls_arc4_crypt( mbedtls_arc4_context *ctx, size_t length, const unsigned char *input,
unsigned char *output );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_ARC4_ALT */
-#include "arc4_alt.h"
-#endif /* MBEDTLS_ARC4_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Checkup routine
*
diff --git a/thirdparty/mbedtls/include/mbedtls/aria.h b/thirdparty/mbedtls/include/mbedtls/aria.h
new file mode 100644
index 0000000000..bae0621b23
--- /dev/null
+++ b/thirdparty/mbedtls/include/mbedtls/aria.h
@@ -0,0 +1,331 @@
+/**
+ * \file aria.h
+ *
+ * \brief ARIA block cipher
+ *
+ * The ARIA algorithm is a symmetric block cipher that can encrypt and
+ * decrypt information. It is defined by the Korean Agency for
+ * Technology and Standards (KATS) in <em>KS X 1213:2004</em> (in
+ * Korean, but see http://210.104.33.10/ARIA/index-e.html in English)
+ * and also described by the IETF in <em>RFC 5794</em>.
+ */
+/* Copyright (C) 2006-2018, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+#ifndef MBEDTLS_ARIA_H
+#define MBEDTLS_ARIA_H
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define MBEDTLS_ARIA_ENCRYPT 1 /**< ARIA encryption. */
+#define MBEDTLS_ARIA_DECRYPT 0 /**< ARIA decryption. */
+
+#define MBEDTLS_ARIA_BLOCKSIZE 16 /**< ARIA block size in bytes. */
+#define MBEDTLS_ARIA_MAX_ROUNDS 16 /**< Maxiumum number of rounds in ARIA. */
+#define MBEDTLS_ARIA_MAX_KEYSIZE 32 /**< Maximum size of an ARIA key in bytes. */
+
+#define MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH -0x005C /**< Invalid key length. */
+#define MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH -0x005E /**< Invalid data input length. */
+#define MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE -0x005A /**< Feature not available. For example, an unsupported ARIA key size. */
+#define MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED -0x0058 /**< ARIA hardware accelerator failed. */
+
+#if !defined(MBEDTLS_ARIA_ALT)
+// Regular implementation
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief The ARIA context-type definition.
+ */
+typedef struct
+{
+ unsigned char nr; /*!< The number of rounds (12, 14 or 16) */
+ /*! The ARIA round keys. */
+ uint32_t rk[MBEDTLS_ARIA_MAX_ROUNDS + 1][MBEDTLS_ARIA_BLOCKSIZE / 4];
+}
+mbedtls_aria_context;
+
+#else /* MBEDTLS_ARIA_ALT */
+#include "aria_alt.h"
+#endif /* MBEDTLS_ARIA_ALT */
+
+/**
+ * \brief This function initializes the specified ARIA context.
+ *
+ * It must be the first API called before using
+ * the context.
+ *
+ * \param ctx The ARIA context to initialize.
+ */
+void mbedtls_aria_init( mbedtls_aria_context *ctx );
+
+/**
+ * \brief This function releases and clears the specified ARIA context.
+ *
+ * \param ctx The ARIA context to clear.
+ */
+void mbedtls_aria_free( mbedtls_aria_context *ctx );
+
+/**
+ * \brief This function sets the encryption key.
+ *
+ * \param ctx The ARIA context to which the key should be bound.
+ * \param key The encryption key.
+ * \param keybits The size of data passed in bits. Valid options are:
+ * <ul><li>128 bits</li>
+ * <li>192 bits</li>
+ * <li>256 bits</li></ul>
+ *
+ * \return \c 0 on success or #MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH
+ * on failure.
+ */
+int mbedtls_aria_setkey_enc( mbedtls_aria_context *ctx,
+ const unsigned char *key,
+ unsigned int keybits );
+
+/**
+ * \brief This function sets the decryption key.
+ *
+ * \param ctx The ARIA context to which the key should be bound.
+ * \param key The decryption key.
+ * \param keybits The size of data passed. Valid options are:
+ * <ul><li>128 bits</li>
+ * <li>192 bits</li>
+ * <li>256 bits</li></ul>
+ *
+ * \return \c 0 on success, or #MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH on failure.
+ */
+int mbedtls_aria_setkey_dec( mbedtls_aria_context *ctx,
+ const unsigned char *key,
+ unsigned int keybits );
+
+/**
+ * \brief This function performs an ARIA single-block encryption or
+ * decryption operation.
+ *
+ * It performs encryption or decryption (depending on whether
+ * the key was set for encryption on decryption) on the input
+ * data buffer defined in the \p input parameter.
+ *
+ * mbedtls_aria_init(), and either mbedtls_aria_setkey_enc() or
+ * mbedtls_aria_setkey_dec() must be called before the first
+ * call to this API with the same context.
+ *
+ * \param ctx The ARIA context to use for encryption or decryption.
+ * \param input The 16-Byte buffer holding the input data.
+ * \param output The 16-Byte buffer holding the output data.
+
+ * \return \c 0 on success.
+ */
+int mbedtls_aria_crypt_ecb( mbedtls_aria_context *ctx,
+ const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE],
+ unsigned char output[MBEDTLS_ARIA_BLOCKSIZE] );
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+/**
+ * \brief This function performs an ARIA-CBC encryption or decryption operation
+ * on full blocks.
+ *
+ * It performs the operation defined in the \p mode
+ * parameter (encrypt/decrypt), on the input data buffer defined in
+ * the \p input parameter.
+ *
+ * It can be called as many times as needed, until all the input
+ * data is processed. mbedtls_aria_init(), and either
+ * mbedtls_aria_setkey_enc() or mbedtls_aria_setkey_dec() must be called
+ * before the first call to this API with the same context.
+ *
+ * \note This function operates on aligned blocks, that is, the input size
+ * must be a multiple of the ARIA block size of 16 Bytes.
+ *
+ * \note Upon exit, the content of the IV is updated so that you can
+ * call the same function again on the next
+ * block(s) of data and get the same result as if it was
+ * encrypted in one call. This allows a "streaming" usage.
+ * If you need to retain the contents of the IV, you should
+ * either save it manually or use the cipher module instead.
+ *
+ *
+ * \param ctx The ARIA context to use for encryption or decryption.
+ * \param mode The ARIA operation: #MBEDTLS_ARIA_ENCRYPT or
+ * #MBEDTLS_ARIA_DECRYPT.
+ * \param length The length of the input data in Bytes. This must be a
+ * multiple of the block size (16 Bytes).
+ * \param iv Initialization vector (updated after use).
+ * \param input The buffer holding the input data.
+ * \param output The buffer holding the output data.
+ *
+ * \return \c 0 on success, or #MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH
+ * on failure.
+ */
+int mbedtls_aria_crypt_cbc( mbedtls_aria_context *ctx,
+ int mode,
+ size_t length,
+ unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output );
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+/**
+ * \brief This function performs an ARIA-CFB128 encryption or decryption
+ * operation.
+ *
+ * It performs the operation defined in the \p mode
+ * parameter (encrypt or decrypt), on the input data buffer
+ * defined in the \p input parameter.
+ *
+ * For CFB, you must set up the context with mbedtls_aria_setkey_enc(),
+ * regardless of whether you are performing an encryption or decryption
+ * operation, that is, regardless of the \p mode parameter. This is
+ * because CFB mode uses the same key schedule for encryption and
+ * decryption.
+ *
+ * \note Upon exit, the content of the IV is updated so that you can
+ * call the same function again on the next
+ * block(s) of data and get the same result as if it was
+ * encrypted in one call. This allows a "streaming" usage.
+ * If you need to retain the contents of the
+ * IV, you must either save it manually or use the cipher
+ * module instead.
+ *
+ *
+ * \param ctx The ARIA context to use for encryption or decryption.
+ * \param mode The ARIA operation: #MBEDTLS_ARIA_ENCRYPT or
+ * #MBEDTLS_ARIA_DECRYPT.
+ * \param length The length of the input data.
+ * \param iv_off The offset in IV (updated after use).
+ * \param iv The initialization vector (updated after use).
+ * \param input The buffer holding the input data.
+ * \param output The buffer holding the output data.
+ *
+ * \return \c 0 on success.
+ */
+int mbedtls_aria_crypt_cfb128( mbedtls_aria_context *ctx,
+ int mode,
+ size_t length,
+ size_t *iv_off,
+ unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output );
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+/**
+ * \brief This function performs an ARIA-CTR encryption or decryption
+ * operation.
+ *
+ * This function performs the operation defined in the \p mode
+ * parameter (encrypt/decrypt), on the input data buffer
+ * defined in the \p input parameter.
+ *
+ * Due to the nature of CTR, you must use the same key schedule
+ * for both encryption and decryption operations. Therefore, you
+ * must use the context initialized with mbedtls_aria_setkey_enc()
+ * for both #MBEDTLS_ARIA_ENCRYPT and #MBEDTLS_ARIA_DECRYPT.
+ *
+ * \warning You must never reuse a nonce value with the same key. Doing so
+ * would void the encryption for the two messages encrypted with
+ * the same nonce and key.
+ *
+ * There are two common strategies for managing nonces with CTR:
+ *
+ * 1. You can handle everything as a single message processed over
+ * successive calls to this function. In that case, you want to
+ * set \p nonce_counter and \p nc_off to 0 for the first call, and
+ * then preserve the values of \p nonce_counter, \p nc_off and \p
+ * stream_block across calls to this function as they will be
+ * updated by this function.
+ *
+ * With this strategy, you must not encrypt more than 2**128
+ * blocks of data with the same key.
+ *
+ * 2. You can encrypt separate messages by dividing the \p
+ * nonce_counter buffer in two areas: the first one used for a
+ * per-message nonce, handled by yourself, and the second one
+ * updated by this function internally.
+ *
+ * For example, you might reserve the first 12 bytes for the
+ * per-message nonce, and the last 4 bytes for internal use. In that
+ * case, before calling this function on a new message you need to
+ * set the first 12 bytes of \p nonce_counter to your chosen nonce
+ * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p
+ * stream_block to be ignored). That way, you can encrypt at most
+ * 2**96 messages of up to 2**32 blocks each with the same key.
+ *
+ * The per-message nonce (or information sufficient to reconstruct
+ * it) needs to be communicated with the ciphertext and must be unique.
+ * The recommended way to ensure uniqueness is to use a message
+ * counter. An alternative is to generate random nonces, but this
+ * limits the number of messages that can be securely encrypted:
+ * for example, with 96-bit random nonces, you should not encrypt
+ * more than 2**32 messages with the same key.
+ *
+ * Note that for both stategies, sizes are measured in blocks and
+ * that an ARIA block is 16 bytes.
+ *
+ * \warning Upon return, \p stream_block contains sensitive data. Its
+ * content must not be written to insecure storage and should be
+ * securely discarded as soon as it's no longer needed.
+ *
+ * \param ctx The ARIA context to use for encryption or decryption.
+ * \param length The length of the input data.
+ * \param nc_off The offset in the current \p stream_block, for
+ * resuming within the current cipher stream. The
+ * offset pointer should be 0 at the start of a stream.
+ * \param nonce_counter The 128-bit nonce and counter.
+ * \param stream_block The saved stream block for resuming. This is
+ * overwritten by the function.
+ * \param input The buffer holding the input data.
+ * \param output The buffer holding the output data.
+ *
+ * \return \c 0 on success.
+ */
+int mbedtls_aria_crypt_ctr( mbedtls_aria_context *ctx,
+ size_t length,
+ size_t *nc_off,
+ unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE],
+ unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output );
+#endif /* MBEDTLS_CIPHER_MODE_CTR */
+
+#if defined(MBEDTLS_SELF_TEST)
+/**
+ * \brief Checkup routine.
+ *
+ * \return \c 0 on success, or \c 1 on failure.
+ */
+int mbedtls_aria_self_test( int verbose );
+#endif /* MBEDTLS_SELF_TEST */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* aria.h */
diff --git a/thirdparty/mbedtls/include/mbedtls/bignum.h b/thirdparty/mbedtls/include/mbedtls/bignum.h
index 3bf02a7ee1..31383b1eb5 100644
--- a/thirdparty/mbedtls/include/mbedtls/bignum.h
+++ b/thirdparty/mbedtls/include/mbedtls/bignum.h
@@ -204,6 +204,8 @@ void mbedtls_mpi_free( mbedtls_mpi *X );
/**
* \brief Enlarge to the specified number of limbs
*
+ * This function does nothing if the MPI is already large enough.
+ *
* \param X MPI to grow
* \param nblimbs The target number of limbs
*
@@ -215,19 +217,23 @@ int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Resize down, keeping at least the specified number of limbs
*
+ * If \c X is smaller than \c nblimbs, it is resized up
+ * instead.
+ *
* \param X MPI to shrink
* \param nblimbs The minimum number of limbs to keep
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * (this can only happen when resizing up).
*/
int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Copy the contents of Y into X
*
- * \param X Destination MPI
- * \param Y Source MPI
+ * \param X Destination MPI. It is enlarged if necessary.
+ * \param Y Source MPI.
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
diff --git a/thirdparty/mbedtls/include/mbedtls/blowfish.h b/thirdparty/mbedtls/include/mbedtls/blowfish.h
index c0ef5a04cc..985faa43f0 100644
--- a/thirdparty/mbedtls/include/mbedtls/blowfish.h
+++ b/thirdparty/mbedtls/include/mbedtls/blowfish.h
@@ -44,14 +44,14 @@
#define MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED -0x0017 /**< Blowfish hardware accelerator failed. */
#define MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH -0x0018 /**< Invalid data input length. */
-#if !defined(MBEDTLS_BLOWFISH_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_BLOWFISH_ALT)
+// Regular implementation
+//
+
/**
* \brief Blowfish context structure
*/
@@ -62,6 +62,10 @@ typedef struct
}
mbedtls_blowfish_context;
+#else /* MBEDTLS_BLOWFISH_ALT */
+#include "blowfish_alt.h"
+#endif /* MBEDTLS_BLOWFISH_ALT */
+
/**
* \brief Initialize Blowfish context
*
@@ -170,7 +174,46 @@ int mbedtls_blowfish_crypt_cfb64( mbedtls_blowfish_context *ctx,
/**
* \brief Blowfish-CTR buffer encryption/decryption
*
- * Warning: You have to keep the maximum use of your counter in mind!
+ * \warning You must never reuse a nonce value with the same key. Doing so
+ * would void the encryption for the two messages encrypted with
+ * the same nonce and key.
+ *
+ * There are two common strategies for managing nonces with CTR:
+ *
+ * 1. You can handle everything as a single message processed over
+ * successive calls to this function. In that case, you want to
+ * set \p nonce_counter and \p nc_off to 0 for the first call, and
+ * then preserve the values of \p nonce_counter, \p nc_off and \p
+ * stream_block across calls to this function as they will be
+ * updated by this function.
+ *
+ * With this strategy, you must not encrypt more than 2**64
+ * blocks of data with the same key.
+ *
+ * 2. You can encrypt separate messages by dividing the \p
+ * nonce_counter buffer in two areas: the first one used for a
+ * per-message nonce, handled by yourself, and the second one
+ * updated by this function internally.
+ *
+ * For example, you might reserve the first 4 bytes for the
+ * per-message nonce, and the last 4 bytes for internal use. In that
+ * case, before calling this function on a new message you need to
+ * set the first 4 bytes of \p nonce_counter to your chosen nonce
+ * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p
+ * stream_block to be ignored). That way, you can encrypt at most
+ * 2**32 messages of up to 2**32 blocks each with the same key.
+ *
+ * The per-message nonce (or information sufficient to reconstruct
+ * it) needs to be communicated with the ciphertext and must be unique.
+ * The recommended way to ensure uniqueness is to use a message
+ * counter.
+ *
+ * Note that for both stategies, sizes are measured in blocks and
+ * that a Blowfish block is 8 bytes.
+ *
+ * \warning Upon return, \p stream_block contains sensitive data. Its
+ * content must not be written to insecure storage and should be
+ * securely discarded as soon as it's no longer needed.
*
* \param ctx Blowfish context
* \param length The length of the data
@@ -198,8 +241,4 @@ int mbedtls_blowfish_crypt_ctr( mbedtls_blowfish_context *ctx,
}
#endif
-#else /* MBEDTLS_BLOWFISH_ALT */
-#include "blowfish_alt.h"
-#endif /* MBEDTLS_BLOWFISH_ALT */
-
#endif /* blowfish.h */
diff --git a/thirdparty/mbedtls/include/mbedtls/bn_mul.h b/thirdparty/mbedtls/include/mbedtls/bn_mul.h
index 354c1cc1ab..f4b2b561d1 100644
--- a/thirdparty/mbedtls/include/mbedtls/bn_mul.h
+++ b/thirdparty/mbedtls/include/mbedtls/bn_mul.h
@@ -521,7 +521,7 @@
"swi r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
- : "r3", "r4" "r5", "r6", "r7", "r8", \
+ : "r3", "r4", "r5", "r6", "r7", "r8", \
"r9", "r10", "r11", "r12", "r13" \
);
diff --git a/thirdparty/mbedtls/include/mbedtls/camellia.h b/thirdparty/mbedtls/include/mbedtls/camellia.h
index cf07629d9b..7e4721af78 100644
--- a/thirdparty/mbedtls/include/mbedtls/camellia.h
+++ b/thirdparty/mbedtls/include/mbedtls/camellia.h
@@ -40,14 +40,14 @@
#define MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026 /**< Invalid data input length. */
#define MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED -0x0027 /**< Camellia hardware accelerator failed. */
-#if !defined(MBEDTLS_CAMELLIA_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_CAMELLIA_ALT)
+// Regular implementation
+//
+
/**
* \brief CAMELLIA context structure
*/
@@ -58,6 +58,10 @@ typedef struct
}
mbedtls_camellia_context;
+#else /* MBEDTLS_CAMELLIA_ALT */
+#include "camellia_alt.h"
+#endif /* MBEDTLS_CAMELLIA_ALT */
+
/**
* \brief Initialize CAMELLIA context
*
@@ -183,12 +187,54 @@ int mbedtls_camellia_crypt_cfb128( mbedtls_camellia_context *ctx,
/**
* \brief CAMELLIA-CTR buffer encryption/decryption
*
- * Warning: You have to keep the maximum use of your counter in mind!
- *
* Note: Due to the nature of CTR you should use the same key schedule for
* both encryption and decryption. So a context initialized with
* mbedtls_camellia_setkey_enc() for both MBEDTLS_CAMELLIA_ENCRYPT and MBEDTLS_CAMELLIA_DECRYPT.
*
+ * \warning You must never reuse a nonce value with the same key. Doing so
+ * would void the encryption for the two messages encrypted with
+ * the same nonce and key.
+ *
+ * There are two common strategies for managing nonces with CTR:
+ *
+ * 1. You can handle everything as a single message processed over
+ * successive calls to this function. In that case, you want to
+ * set \p nonce_counter and \p nc_off to 0 for the first call, and
+ * then preserve the values of \p nonce_counter, \p nc_off and \p
+ * stream_block across calls to this function as they will be
+ * updated by this function.
+ *
+ * With this strategy, you must not encrypt more than 2**128
+ * blocks of data with the same key.
+ *
+ * 2. You can encrypt separate messages by dividing the \p
+ * nonce_counter buffer in two areas: the first one used for a
+ * per-message nonce, handled by yourself, and the second one
+ * updated by this function internally.
+ *
+ * For example, you might reserve the first 12 bytes for the
+ * per-message nonce, and the last 4 bytes for internal use. In that
+ * case, before calling this function on a new message you need to
+ * set the first 12 bytes of \p nonce_counter to your chosen nonce
+ * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p
+ * stream_block to be ignored). That way, you can encrypt at most
+ * 2**96 messages of up to 2**32 blocks each with the same key.
+ *
+ * The per-message nonce (or information sufficient to reconstruct
+ * it) needs to be communicated with the ciphertext and must be unique.
+ * The recommended way to ensure uniqueness is to use a message
+ * counter. An alternative is to generate random nonces, but this
+ * limits the number of messages that can be securely encrypted:
+ * for example, with 96-bit random nonces, you should not encrypt
+ * more than 2**32 messages with the same key.
+ *
+ * Note that for both stategies, sizes are measured in blocks and
+ * that a CAMELLIA block is 16 bytes.
+ *
+ * \warning Upon return, \p stream_block contains sensitive data. Its
+ * content must not be written to insecure storage and should be
+ * securely discarded as soon as it's no longer needed.
+ *
* \param ctx CAMELLIA context
* \param length The length of the data
* \param nc_off The offset in the current stream_block (for resuming
@@ -211,18 +257,6 @@ int mbedtls_camellia_crypt_ctr( mbedtls_camellia_context *ctx,
unsigned char *output );
#endif /* MBEDTLS_CIPHER_MODE_CTR */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_CAMELLIA_ALT */
-#include "camellia_alt.h"
-#endif /* MBEDTLS_CAMELLIA_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Checkup routine
*
diff --git a/thirdparty/mbedtls/include/mbedtls/ccm.h b/thirdparty/mbedtls/include/mbedtls/ccm.h
index 630b7fdf6c..8585ce5e7c 100644
--- a/thirdparty/mbedtls/include/mbedtls/ccm.h
+++ b/thirdparty/mbedtls/include/mbedtls/ccm.h
@@ -1,8 +1,11 @@
/**
* \file ccm.h
*
- * \brief CCM combines Counter mode encryption with CBC-MAC authentication
- * for 128-bit block ciphers.
+ * \brief This file provides an API for the CCM authenticated encryption
+ * mode for block ciphers.
+ *
+ * CCM combines Counter mode encryption with CBC-MAC authentication
+ * for 128-bit block ciphers.
*
* Input to CCM includes the following elements:
* <ul><li>Payload - data that is both authenticated and encrypted.</li>
@@ -40,14 +43,15 @@
#define MBEDTLS_ERR_CCM_AUTH_FAILED -0x000F /**< Authenticated decryption failed. */
#define MBEDTLS_ERR_CCM_HW_ACCEL_FAILED -0x0011 /**< CCM hardware accelerator failed. */
-#if !defined(MBEDTLS_CCM_ALT)
-// Regular implementation
-//
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_CCM_ALT)
+// Regular implementation
+//
+
/**
* \brief The CCM context-type definition. The CCM context is passed
* to the APIs called.
@@ -57,6 +61,10 @@ typedef struct {
}
mbedtls_ccm_context;
+#else /* MBEDTLS_CCM_ALT */
+#include "ccm_alt.h"
+#endif /* MBEDTLS_CCM_ALT */
+
/**
* \brief This function initializes the specified CCM context,
* to make references valid, and prepare the context
@@ -75,7 +83,8 @@ void mbedtls_ccm_init( mbedtls_ccm_context *ctx );
* \param key The encryption key.
* \param keybits The key size in bits. This must be acceptable by the cipher.
*
- * \return \c 0 on success, or a cipher-specific error code.
+ * \return \c 0 on success.
+ * \return A CCM or cipher-specific error code on failure.
*/
int mbedtls_ccm_setkey( mbedtls_ccm_context *ctx,
mbedtls_cipher_id_t cipher,
@@ -93,6 +102,13 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx );
/**
* \brief This function encrypts a buffer using CCM.
*
+ *
+ * \note The tag is written to a separate buffer. To concatenate
+ * the \p tag with the \p output, as done in <em>RFC-3610:
+ * Counter with CBC-MAC (CCM)</em>, use
+ * \p tag = \p output + \p length, and make sure that the
+ * output buffer is at least \p length + \p tag_len wide.
+ *
* \param ctx The CCM context to use for encryption.
* \param length The length of the input data in Bytes.
* \param iv Initialization vector (nonce).
@@ -107,13 +123,8 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx );
* \param tag_len The length of the tag to generate in Bytes:
* 4, 6, 8, 10, 12, 14 or 16.
*
- * \note The tag is written to a separate buffer. To concatenate
- * the \p tag with the \p output, as done in <em>RFC-3610:
- * Counter with CBC-MAC (CCM)</em>, use
- * \p tag = \p output + \p length, and make sure that the
- * output buffer is at least \p length + \p tag_len wide.
- *
* \return \c 0 on success.
+ * \return A CCM or cipher-specific error code on failure.
*/
int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
const unsigned char *iv, size_t iv_len,
@@ -139,8 +150,9 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
* \param tag_len The length of the tag in Bytes.
* 4, 6, 8, 10, 12, 14 or 16.
*
- * \return 0 if successful and authenticated, or
- * #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
+ * \return \c 0 on success. This indicates that the message is authentic.
+ * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
+ * \return A cipher-specific error code on calculation failure.
*/
int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
const unsigned char *iv, size_t iv_len,
@@ -148,23 +160,13 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
const unsigned char *input, unsigned char *output,
const unsigned char *tag, size_t tag_len );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_CCM_ALT */
-#include "ccm_alt.h"
-#endif /* MBEDTLS_CCM_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C)
/**
* \brief The CCM checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_ccm_self_test( int verbose );
#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */
diff --git a/thirdparty/mbedtls/include/mbedtls/cipher.h b/thirdparty/mbedtls/include/mbedtls/cipher.h
index d1f4efef8e..46b3bdfefa 100644
--- a/thirdparty/mbedtls/include/mbedtls/cipher.h
+++ b/thirdparty/mbedtls/include/mbedtls/cipher.h
@@ -1,7 +1,9 @@
/**
* \file cipher.h
*
- * \brief The generic cipher wrapper.
+ * \brief This file contains an abstraction interface for use with the cipher
+ * primitives provided by the library. It provides a common interface to all of
+ * the available cipher operations.
*
* \author Adriaan de Jong <dejong@fox-it.com>
*/
@@ -69,93 +71,112 @@ extern "C" {
#endif
/**
- * \brief An enumeration of supported ciphers.
+ * \brief Supported cipher types.
*
- * \warning ARC4 and DES are considered weak ciphers and their use
- * constitutes a security risk. We recommend considering stronger
+ * \warning RC4 and DES are considered weak ciphers and their use
+ * constitutes a security risk. Arm recommends considering stronger
* ciphers instead.
*/
typedef enum {
- MBEDTLS_CIPHER_ID_NONE = 0,
- MBEDTLS_CIPHER_ID_NULL,
- MBEDTLS_CIPHER_ID_AES,
- MBEDTLS_CIPHER_ID_DES,
- MBEDTLS_CIPHER_ID_3DES,
- MBEDTLS_CIPHER_ID_CAMELLIA,
- MBEDTLS_CIPHER_ID_BLOWFISH,
- MBEDTLS_CIPHER_ID_ARC4,
+ MBEDTLS_CIPHER_ID_NONE = 0, /**< Placeholder to mark the end of cipher ID lists. */
+ MBEDTLS_CIPHER_ID_NULL, /**< The identity cipher, treated as a stream cipher. */
+ MBEDTLS_CIPHER_ID_AES, /**< The AES cipher. */
+ MBEDTLS_CIPHER_ID_DES, /**< The DES cipher. */
+ MBEDTLS_CIPHER_ID_3DES, /**< The Triple DES cipher. */
+ MBEDTLS_CIPHER_ID_CAMELLIA, /**< The Camellia cipher. */
+ MBEDTLS_CIPHER_ID_BLOWFISH, /**< The Blowfish cipher. */
+ MBEDTLS_CIPHER_ID_ARC4, /**< The RC4 cipher. */
+ MBEDTLS_CIPHER_ID_ARIA, /**< The Aria cipher. */
} mbedtls_cipher_id_t;
/**
- * \brief An enumeration of supported (cipher, mode) pairs.
+ * \brief Supported {cipher type, cipher mode} pairs.
*
- * \warning ARC4 and DES are considered weak ciphers and their use
- * constitutes a security risk. We recommend considering stronger
+ * \warning RC4 and DES are considered weak ciphers and their use
+ * constitutes a security risk. Arm recommends considering stronger
* ciphers instead.
*/
typedef enum {
- MBEDTLS_CIPHER_NONE = 0,
- MBEDTLS_CIPHER_NULL,
- MBEDTLS_CIPHER_AES_128_ECB,
- MBEDTLS_CIPHER_AES_192_ECB,
- MBEDTLS_CIPHER_AES_256_ECB,
- MBEDTLS_CIPHER_AES_128_CBC,
- MBEDTLS_CIPHER_AES_192_CBC,
- MBEDTLS_CIPHER_AES_256_CBC,
- MBEDTLS_CIPHER_AES_128_CFB128,
- MBEDTLS_CIPHER_AES_192_CFB128,
- MBEDTLS_CIPHER_AES_256_CFB128,
- MBEDTLS_CIPHER_AES_128_CTR,
- MBEDTLS_CIPHER_AES_192_CTR,
- MBEDTLS_CIPHER_AES_256_CTR,
- MBEDTLS_CIPHER_AES_128_GCM,
- MBEDTLS_CIPHER_AES_192_GCM,
- MBEDTLS_CIPHER_AES_256_GCM,
- MBEDTLS_CIPHER_CAMELLIA_128_ECB,
- MBEDTLS_CIPHER_CAMELLIA_192_ECB,
- MBEDTLS_CIPHER_CAMELLIA_256_ECB,
- MBEDTLS_CIPHER_CAMELLIA_128_CBC,
- MBEDTLS_CIPHER_CAMELLIA_192_CBC,
- MBEDTLS_CIPHER_CAMELLIA_256_CBC,
- MBEDTLS_CIPHER_CAMELLIA_128_CFB128,
- MBEDTLS_CIPHER_CAMELLIA_192_CFB128,
- MBEDTLS_CIPHER_CAMELLIA_256_CFB128,
- MBEDTLS_CIPHER_CAMELLIA_128_CTR,
- MBEDTLS_CIPHER_CAMELLIA_192_CTR,
- MBEDTLS_CIPHER_CAMELLIA_256_CTR,
- MBEDTLS_CIPHER_CAMELLIA_128_GCM,
- MBEDTLS_CIPHER_CAMELLIA_192_GCM,
- MBEDTLS_CIPHER_CAMELLIA_256_GCM,
- MBEDTLS_CIPHER_DES_ECB,
- MBEDTLS_CIPHER_DES_CBC,
- MBEDTLS_CIPHER_DES_EDE_ECB,
- MBEDTLS_CIPHER_DES_EDE_CBC,
- MBEDTLS_CIPHER_DES_EDE3_ECB,
- MBEDTLS_CIPHER_DES_EDE3_CBC,
- MBEDTLS_CIPHER_BLOWFISH_ECB,
- MBEDTLS_CIPHER_BLOWFISH_CBC,
- MBEDTLS_CIPHER_BLOWFISH_CFB64,
- MBEDTLS_CIPHER_BLOWFISH_CTR,
- MBEDTLS_CIPHER_ARC4_128,
- MBEDTLS_CIPHER_AES_128_CCM,
- MBEDTLS_CIPHER_AES_192_CCM,
- MBEDTLS_CIPHER_AES_256_CCM,
- MBEDTLS_CIPHER_CAMELLIA_128_CCM,
- MBEDTLS_CIPHER_CAMELLIA_192_CCM,
- MBEDTLS_CIPHER_CAMELLIA_256_CCM,
+ MBEDTLS_CIPHER_NONE = 0, /**< Placeholder to mark the end of cipher-pair lists. */
+ MBEDTLS_CIPHER_NULL, /**< The identity stream cipher. */
+ MBEDTLS_CIPHER_AES_128_ECB, /**< AES cipher with 128-bit ECB mode. */
+ MBEDTLS_CIPHER_AES_192_ECB, /**< AES cipher with 192-bit ECB mode. */
+ MBEDTLS_CIPHER_AES_256_ECB, /**< AES cipher with 256-bit ECB mode. */
+ MBEDTLS_CIPHER_AES_128_CBC, /**< AES cipher with 128-bit CBC mode. */
+ MBEDTLS_CIPHER_AES_192_CBC, /**< AES cipher with 192-bit CBC mode. */
+ MBEDTLS_CIPHER_AES_256_CBC, /**< AES cipher with 256-bit CBC mode. */
+ MBEDTLS_CIPHER_AES_128_CFB128, /**< AES cipher with 128-bit CFB128 mode. */
+ MBEDTLS_CIPHER_AES_192_CFB128, /**< AES cipher with 192-bit CFB128 mode. */
+ MBEDTLS_CIPHER_AES_256_CFB128, /**< AES cipher with 256-bit CFB128 mode. */
+ MBEDTLS_CIPHER_AES_128_CTR, /**< AES cipher with 128-bit CTR mode. */
+ MBEDTLS_CIPHER_AES_192_CTR, /**< AES cipher with 192-bit CTR mode. */
+ MBEDTLS_CIPHER_AES_256_CTR, /**< AES cipher with 256-bit CTR mode. */
+ MBEDTLS_CIPHER_AES_128_GCM, /**< AES cipher with 128-bit GCM mode. */
+ MBEDTLS_CIPHER_AES_192_GCM, /**< AES cipher with 192-bit GCM mode. */
+ MBEDTLS_CIPHER_AES_256_GCM, /**< AES cipher with 256-bit GCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_ECB, /**< Camellia cipher with 128-bit ECB mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_ECB, /**< Camellia cipher with 192-bit ECB mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_ECB, /**< Camellia cipher with 256-bit ECB mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_CBC, /**< Camellia cipher with 128-bit CBC mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_CBC, /**< Camellia cipher with 192-bit CBC mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_CBC, /**< Camellia cipher with 256-bit CBC mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_CFB128, /**< Camellia cipher with 128-bit CFB128 mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_CFB128, /**< Camellia cipher with 192-bit CFB128 mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_CFB128, /**< Camellia cipher with 256-bit CFB128 mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_CTR, /**< Camellia cipher with 128-bit CTR mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_CTR, /**< Camellia cipher with 192-bit CTR mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_CTR, /**< Camellia cipher with 256-bit CTR mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_GCM, /**< Camellia cipher with 128-bit GCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_GCM, /**< Camellia cipher with 192-bit GCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_GCM, /**< Camellia cipher with 256-bit GCM mode. */
+ MBEDTLS_CIPHER_DES_ECB, /**< DES cipher with ECB mode. */
+ MBEDTLS_CIPHER_DES_CBC, /**< DES cipher with CBC mode. */
+ MBEDTLS_CIPHER_DES_EDE_ECB, /**< DES cipher with EDE ECB mode. */
+ MBEDTLS_CIPHER_DES_EDE_CBC, /**< DES cipher with EDE CBC mode. */
+ MBEDTLS_CIPHER_DES_EDE3_ECB, /**< DES cipher with EDE3 ECB mode. */
+ MBEDTLS_CIPHER_DES_EDE3_CBC, /**< DES cipher with EDE3 CBC mode. */
+ MBEDTLS_CIPHER_BLOWFISH_ECB, /**< Blowfish cipher with ECB mode. */
+ MBEDTLS_CIPHER_BLOWFISH_CBC, /**< Blowfish cipher with CBC mode. */
+ MBEDTLS_CIPHER_BLOWFISH_CFB64, /**< Blowfish cipher with CFB64 mode. */
+ MBEDTLS_CIPHER_BLOWFISH_CTR, /**< Blowfish cipher with CTR mode. */
+ MBEDTLS_CIPHER_ARC4_128, /**< RC4 cipher with 128-bit mode. */
+ MBEDTLS_CIPHER_AES_128_CCM, /**< AES cipher with 128-bit CCM mode. */
+ MBEDTLS_CIPHER_AES_192_CCM, /**< AES cipher with 192-bit CCM mode. */
+ MBEDTLS_CIPHER_AES_256_CCM, /**< AES cipher with 256-bit CCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_128_CCM, /**< Camellia cipher with 128-bit CCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_192_CCM, /**< Camellia cipher with 192-bit CCM mode. */
+ MBEDTLS_CIPHER_CAMELLIA_256_CCM, /**< Camellia cipher with 256-bit CCM mode. */
+ MBEDTLS_CIPHER_ARIA_128_ECB, /**< Aria cipher with 128-bit key and ECB mode. */
+ MBEDTLS_CIPHER_ARIA_192_ECB, /**< Aria cipher with 192-bit key and ECB mode. */
+ MBEDTLS_CIPHER_ARIA_256_ECB, /**< Aria cipher with 256-bit key and ECB mode. */
+ MBEDTLS_CIPHER_ARIA_128_CBC, /**< Aria cipher with 128-bit key and CBC mode. */
+ MBEDTLS_CIPHER_ARIA_192_CBC, /**< Aria cipher with 192-bit key and CBC mode. */
+ MBEDTLS_CIPHER_ARIA_256_CBC, /**< Aria cipher with 256-bit key and CBC mode. */
+ MBEDTLS_CIPHER_ARIA_128_CFB128, /**< Aria cipher with 128-bit key and CFB-128 mode. */
+ MBEDTLS_CIPHER_ARIA_192_CFB128, /**< Aria cipher with 192-bit key and CFB-128 mode. */
+ MBEDTLS_CIPHER_ARIA_256_CFB128, /**< Aria cipher with 256-bit key and CFB-128 mode. */
+ MBEDTLS_CIPHER_ARIA_128_CTR, /**< Aria cipher with 128-bit key and CTR mode. */
+ MBEDTLS_CIPHER_ARIA_192_CTR, /**< Aria cipher with 192-bit key and CTR mode. */
+ MBEDTLS_CIPHER_ARIA_256_CTR, /**< Aria cipher with 256-bit key and CTR mode. */
+ MBEDTLS_CIPHER_ARIA_128_GCM, /**< Aria cipher with 128-bit key and GCM mode. */
+ MBEDTLS_CIPHER_ARIA_192_GCM, /**< Aria cipher with 192-bit key and GCM mode. */
+ MBEDTLS_CIPHER_ARIA_256_GCM, /**< Aria cipher with 256-bit key and GCM mode. */
+ MBEDTLS_CIPHER_ARIA_128_CCM, /**< Aria cipher with 128-bit key and CCM mode. */
+ MBEDTLS_CIPHER_ARIA_192_CCM, /**< Aria cipher with 192-bit key and CCM mode. */
+ MBEDTLS_CIPHER_ARIA_256_CCM, /**< Aria cipher with 256-bit key and CCM mode. */
} mbedtls_cipher_type_t;
/** Supported cipher modes. */
typedef enum {
- MBEDTLS_MODE_NONE = 0,
- MBEDTLS_MODE_ECB,
- MBEDTLS_MODE_CBC,
- MBEDTLS_MODE_CFB,
- MBEDTLS_MODE_OFB, /* Unused! */
- MBEDTLS_MODE_CTR,
- MBEDTLS_MODE_GCM,
- MBEDTLS_MODE_STREAM,
- MBEDTLS_MODE_CCM,
+ MBEDTLS_MODE_NONE = 0, /**< None. */
+ MBEDTLS_MODE_ECB, /**< The ECB cipher mode. */
+ MBEDTLS_MODE_CBC, /**< The CBC cipher mode. */
+ MBEDTLS_MODE_CFB, /**< The CFB cipher mode. */
+ MBEDTLS_MODE_OFB, /**< The OFB cipher mode - unsupported. */
+ MBEDTLS_MODE_CTR, /**< The CTR cipher mode. */
+ MBEDTLS_MODE_GCM, /**< The GCM cipher mode. */
+ MBEDTLS_MODE_STREAM, /**< The stream cipher mode. */
+ MBEDTLS_MODE_CCM, /**< The CCM cipher mode. */
} mbedtls_cipher_mode_t;
/** Supported cipher padding types. */
@@ -163,8 +184,8 @@ typedef enum {
MBEDTLS_PADDING_PKCS7 = 0, /**< PKCS7 padding (default). */
MBEDTLS_PADDING_ONE_AND_ZEROS, /**< ISO/IEC 7816-4 padding. */
MBEDTLS_PADDING_ZEROS_AND_LEN, /**< ANSI X.923 padding. */
- MBEDTLS_PADDING_ZEROS, /**< zero padding (not reversible). */
- MBEDTLS_PADDING_NONE, /**< never pad (full blocks only). */
+ MBEDTLS_PADDING_ZEROS, /**< Zero padding (not reversible). */
+ MBEDTLS_PADDING_NONE, /**< Never pad (full blocks only). */
} mbedtls_cipher_padding_t;
/** Type of operation. */
@@ -228,7 +249,10 @@ typedef struct {
*/
unsigned int iv_size;
- /** Flags to set. For example, if the cipher supports variable IV sizes or variable key sizes. */
+ /** Bitflag comprised of MBEDTLS_CIPHER_VARIABLE_IV_LEN and
+ * MBEDTLS_CIPHER_VARIABLE_KEY_LEN indicating whether the
+ * cipher supports variable IV or variable key sizes, respectively.
+ */
int flags;
/** The block size, in Bytes. */
@@ -299,7 +323,8 @@ const int *mbedtls_cipher_list( void );
* \param cipher_name Name of the cipher to search for.
*
* \return The cipher information structure associated with the
- * given \p cipher_name, or NULL if not found.
+ * given \p cipher_name.
+ * \return NULL if the associated cipher information is not found.
*/
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string( const char *cipher_name );
@@ -310,7 +335,8 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string( const char *cipher
* \param cipher_type Type of the cipher to search for.
*
* \return The cipher information structure associated with the
- * given \p cipher_type, or NULL if not found.
+ * given \p cipher_type.
+ * \return NULL if the associated cipher information is not found.
*/
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type( const mbedtls_cipher_type_t cipher_type );
@@ -325,7 +351,8 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type( const mbedtls_cipher
* \param mode The cipher mode. For example, #MBEDTLS_MODE_CBC.
*
* \return The cipher information structure associated with the
- * given \p cipher_id, or NULL if not found.
+ * given \p cipher_id.
+ * \return NULL if the associated cipher information is not found.
*/
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values( const mbedtls_cipher_id_t cipher_id,
int key_bitlen,
@@ -352,10 +379,11 @@ void mbedtls_cipher_free( mbedtls_cipher_context_t *ctx );
* \param ctx The context to initialize. May not be NULL.
* \param cipher_info The cipher to use.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on parameter failure,
- * #MBEDTLS_ERR_CIPHER_ALLOC_FAILED if allocation of the
- * cipher-specific context failed.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_ALLOC_FAILED if allocation of the
+ * cipher-specific context fails.
*
* \internal Currently, the function also clears the structure.
* In future versions, the caller will be required to call
@@ -368,8 +396,8 @@ int mbedtls_cipher_setup( mbedtls_cipher_context_t *ctx, const mbedtls_cipher_in
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The size of the blocks of the cipher, or zero if \p ctx
- * has not been initialized.
+ * \return The size of the blocks of the cipher.
+ * \return 0 if \p ctx has not been initialized.
*/
static inline unsigned int mbedtls_cipher_get_block_size( const mbedtls_cipher_context_t *ctx )
{
@@ -385,8 +413,8 @@ static inline unsigned int mbedtls_cipher_get_block_size( const mbedtls_cipher_c
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The mode of operation, or #MBEDTLS_MODE_NONE if
- * \p ctx has not been initialized.
+ * \return The mode of operation.
+ * \return #MBEDTLS_MODE_NONE if \p ctx has not been initialized.
*/
static inline mbedtls_cipher_mode_t mbedtls_cipher_get_cipher_mode( const mbedtls_cipher_context_t *ctx )
{
@@ -402,9 +430,9 @@ static inline mbedtls_cipher_mode_t mbedtls_cipher_get_cipher_mode( const mbedtl
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return <ul><li>If no IV has been set: the recommended IV size.
- * 0 for ciphers not using IV or nonce.</li>
- * <li>If IV has already been set: the actual size.</li></ul>
+ * \return The recommended IV size if no IV has been set.
+ * \return \c 0 for ciphers not using an IV or a nonce.
+ * \return The actual size if an IV has been set.
*/
static inline int mbedtls_cipher_get_iv_size( const mbedtls_cipher_context_t *ctx )
{
@@ -422,8 +450,8 @@ static inline int mbedtls_cipher_get_iv_size( const mbedtls_cipher_context_t *ct
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The type of the cipher, or #MBEDTLS_CIPHER_NONE if
- * \p ctx has not been initialized.
+ * \return The type of the cipher.
+ * \return #MBEDTLS_CIPHER_NONE if \p ctx has not been initialized.
*/
static inline mbedtls_cipher_type_t mbedtls_cipher_get_type( const mbedtls_cipher_context_t *ctx )
{
@@ -439,8 +467,8 @@ static inline mbedtls_cipher_type_t mbedtls_cipher_get_type( const mbedtls_ciphe
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The name of the cipher, or NULL if \p ctx has not
- * been not initialized.
+ * \return The name of the cipher.
+ * \return NULL if \p ctx has not been not initialized.
*/
static inline const char *mbedtls_cipher_get_name( const mbedtls_cipher_context_t *ctx )
{
@@ -455,8 +483,8 @@ static inline const char *mbedtls_cipher_get_name( const mbedtls_cipher_context_
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The key length of the cipher in bits, or
- * #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been
+ * \return The key length of the cipher in bits.
+ * \return #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been
* initialized.
*/
static inline int mbedtls_cipher_get_key_bitlen( const mbedtls_cipher_context_t *ctx )
@@ -472,9 +500,8 @@ static inline int mbedtls_cipher_get_key_bitlen( const mbedtls_cipher_context_t
*
* \param ctx The context of the cipher. Must be initialized.
*
- * \return The type of operation: #MBEDTLS_ENCRYPT or
- * #MBEDTLS_DECRYPT, or #MBEDTLS_OPERATION_NONE if \p ctx
- * has not been initialized.
+ * \return The type of operation: #MBEDTLS_ENCRYPT or #MBEDTLS_DECRYPT.
+ * \return #MBEDTLS_OPERATION_NONE if \p ctx has not been initialized.
*/
static inline mbedtls_operation_t mbedtls_cipher_get_operation( const mbedtls_cipher_context_t *ctx )
{
@@ -495,9 +522,10 @@ static inline mbedtls_operation_t mbedtls_cipher_get_operation( const mbedtls_ci
* \param operation The operation that the key will be used for:
* #MBEDTLS_ENCRYPT or #MBEDTLS_DECRYPT.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if
- * parameter verification fails, or a cipher-specific
- * error code.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_setkey( mbedtls_cipher_context_t *ctx, const unsigned char *key,
int key_bitlen, const mbedtls_operation_t operation );
@@ -512,9 +540,10 @@ int mbedtls_cipher_setkey( mbedtls_cipher_context_t *ctx, const unsigned char *k
* \param ctx The generic cipher context.
* \param mode The padding mode.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
- * if the selected padding mode is not supported, or
- * #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if the cipher mode
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
+ * if the selected padding mode is not supported.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if the cipher mode
* does not support padding.
*/
int mbedtls_cipher_set_padding_mode( mbedtls_cipher_context_t *ctx, mbedtls_cipher_padding_t mode );
@@ -524,15 +553,17 @@ int mbedtls_cipher_set_padding_mode( mbedtls_cipher_context_t *ctx, mbedtls_ciph
* \brief This function sets the initialization vector (IV)
* or nonce.
*
+ * \note Some ciphers do not use IVs nor nonce. For these
+ * ciphers, this function has no effect.
+ *
* \param ctx The generic cipher context.
* \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers.
* \param iv_len The IV length for ciphers with variable-size IV.
* This parameter is discarded by ciphers with fixed-size IV.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA
- *
- * \note Some ciphers do not use IVs nor nonce. For these
- * ciphers, this function has no effect.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
*/
int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx,
const unsigned char *iv, size_t iv_len );
@@ -542,8 +573,9 @@ int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx,
*
* \param ctx The generic cipher context.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA
- * if parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
*/
int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx );
@@ -557,7 +589,8 @@ int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx );
* \param ad The additional data to use.
* \param ad_len the Length of \p ad.
*
- * \return \c 0 on success, or a specific error code on failure.
+ * \return \c 0 on success.
+ * \return A specific error code on failure.
*/
int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
const unsigned char *ad, size_t ad_len );
@@ -573,6 +606,11 @@ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
* Exception: For MBEDTLS_MODE_ECB, expects a single block
* in size. For example, 16 Bytes for AES.
*
+ * \note If the underlying cipher is used in GCM mode, all calls
+ * to this function, except for the last one before
+ * mbedtls_cipher_finish(), must have \p ilen as a
+ * multiple of the block size of the cipher.
+ *
* \param ctx The generic cipher context.
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
@@ -582,16 +620,12 @@ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
* \param olen The length of the output data, to be updated with the
* actual number of Bytes written.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if
- * parameter verification fails,
- * #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE on an
- * unsupported mode for a cipher, or a cipher-specific
- * error code.
- *
- * \note If the underlying cipher is GCM, all calls to this
- * function, except the last one before
- * mbedtls_cipher_finish(). Must have \p ilen as a
- * multiple of the block_size.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE on an
+ * unsupported mode for a cipher.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *input,
size_t ilen, unsigned char *output, size_t *olen );
@@ -606,13 +640,14 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
* \param output The buffer to write data to. Needs block_size available.
* \param olen The length of the data written to the \p output buffer.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if
- * parameter verification fails,
- * #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED if decryption
- * expected a full block but was not provided one,
- * #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding
- * while decrypting, or a cipher-specific error code
- * on failure for any other reason.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption
+ * expecting a full block but not receiving one.
+ * \return #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding
+ * while decrypting.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx,
unsigned char *output, size_t *olen );
@@ -627,7 +662,8 @@ int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx,
* \param tag The buffer to write the tag to.
* \param tag_len The length of the tag to write.
*
- * \return \c 0 on success, or a specific error code on failure.
+ * \return \c 0 on success.
+ * \return A specific error code on failure.
*/
int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx,
unsigned char *tag, size_t tag_len );
@@ -641,7 +677,8 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx,
* \param tag The buffer holding the tag.
* \param tag_len The length of the tag to check.
*
- * \return \c 0 on success, or a specific error code on failure.
+ * \return \c 0 on success.
+ * \return A specific error code on failure.
*/
int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
const unsigned char *tag, size_t tag_len );
@@ -667,13 +704,14 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
* \note Some ciphers do not use IVs nor nonce. For these
* ciphers, use \p iv = NULL and \p iv_len = 0.
*
- * \returns \c 0 on success, or
- * #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA, or
- * #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED if decryption
- * expected a full block but was not provided one, or
- * #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding
- * while decrypting, or a cipher-specific error code on
- * failure for any other reason.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption
+ * expecting a full block but not receiving one.
+ * \return #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding
+ * while decrypting.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_crypt( mbedtls_cipher_context_t *ctx,
const unsigned char *iv, size_t iv_len,
@@ -699,9 +737,10 @@ int mbedtls_cipher_crypt( mbedtls_cipher_context_t *ctx,
* \param tag The buffer for the authentication tag.
* \param tag_len The desired length of the authentication tag.
*
- * \returns \c 0 on success, or
- * #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA, or
- * a cipher-specific error code.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx,
const unsigned char *iv, size_t iv_len,
@@ -713,6 +752,10 @@ int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx,
/**
* \brief The generic autenticated decryption (AEAD) function.
*
+ * \note If the data is not authentic, then the output buffer
+ * is zeroed out to prevent the unauthentic plaintext being
+ * used, making this interface safer.
+ *
* \param ctx The generic cipher context.
* \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers.
* \param iv_len The IV length for ciphers with variable-size IV.
@@ -728,14 +771,11 @@ int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx,
* \param tag The buffer holding the authentication tag.
* \param tag_len The length of the authentication tag.
*
- * \returns \c 0 on success, or
- * #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA, or
- * #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic,
- * or a cipher-specific error code on failure for any other reason.
- *
- * \note If the data is not authentic, then the output buffer
- * is zeroed out to prevent the unauthentic plaintext being
- * used, making this interface safer.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_auth_decrypt( mbedtls_cipher_context_t *ctx,
const unsigned char *iv, size_t iv_len,
diff --git a/thirdparty/mbedtls/include/mbedtls/cmac.h b/thirdparty/mbedtls/include/mbedtls/cmac.h
index 628c9daba2..913c05f8a7 100644
--- a/thirdparty/mbedtls/include/mbedtls/cmac.h
+++ b/thirdparty/mbedtls/include/mbedtls/cmac.h
@@ -1,8 +1,10 @@
/**
* \file cmac.h
*
- * \brief The Cipher-based Message Authentication Code (CMAC) Mode for
- * Authentication.
+ * \brief This file contains CMAC definitions and functions.
+ *
+ * The Cipher-based Message Authentication Code (CMAC) Mode for
+ * Authentication is defined in <em>RFC-4493: The AES-CMAC Algorithm</em>.
*/
/*
* Copyright (C) 2015-2018, Arm Limited (or its affiliates), All Rights Reserved
@@ -38,9 +40,9 @@ extern "C" {
#define MBEDTLS_DES3_BLOCK_SIZE 8
#if defined(MBEDTLS_AES_C)
-#define MBEDTLS_CIPHER_BLKSIZE_MAX 16 /* The longest block used by CMAC is that of AES. */
+#define MBEDTLS_CIPHER_BLKSIZE_MAX 16 /**< The longest block used by CMAC is that of AES. */
#else
-#define MBEDTLS_CIPHER_BLKSIZE_MAX 8 /* The longest block used by CMAC is that of 3DES. */
+#define MBEDTLS_CIPHER_BLKSIZE_MAX 8 /**< The longest block used by CMAC is that of 3DES. */
#endif
#if !defined(MBEDTLS_CMAC_ALT)
@@ -61,22 +63,25 @@ struct mbedtls_cmac_context_t
size_t unprocessed_len;
};
+#else /* !MBEDTLS_CMAC_ALT */
+#include "cmac_alt.h"
+#endif /* !MBEDTLS_CMAC_ALT */
+
/**
* \brief This function sets the CMAC key, and prepares to authenticate
* the input data.
* Must be called with an initialized cipher context.
*
* \param ctx The cipher context used for the CMAC operation, initialized
- * as one of the following types:<ul>
- * <li>MBEDTLS_CIPHER_AES_128_ECB</li>
- * <li>MBEDTLS_CIPHER_AES_192_ECB</li>
- * <li>MBEDTLS_CIPHER_AES_256_ECB</li>
- * <li>MBEDTLS_CIPHER_DES_EDE3_ECB</li></ul>
+ * as one of the following types: MBEDTLS_CIPHER_AES_128_ECB,
+ * MBEDTLS_CIPHER_AES_192_ECB, MBEDTLS_CIPHER_AES_256_ECB,
+ * or MBEDTLS_CIPHER_DES_EDE3_ECB.
* \param key The CMAC key.
* \param keybits The length of the CMAC key in bits.
* Must be supported by the cipher.
*
- * \return \c 0 on success, or a cipher-specific error code.
+ * \return \c 0 on success.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_cmac_starts( mbedtls_cipher_context_t *ctx,
const unsigned char *key, size_t keybits );
@@ -93,8 +98,9 @@ int mbedtls_cipher_cmac_starts( mbedtls_cipher_context_t *ctx,
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA
- * if parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA
+ * if parameter verification fails.
*/
int mbedtls_cipher_cmac_update( mbedtls_cipher_context_t *ctx,
const unsigned char *input, size_t ilen );
@@ -110,7 +116,8 @@ int mbedtls_cipher_cmac_update( mbedtls_cipher_context_t *ctx,
* \param ctx The cipher context used for the CMAC operation.
* \param output The output buffer for the CMAC checksum result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA
* if parameter verification fails.
*/
int mbedtls_cipher_cmac_finish( mbedtls_cipher_context_t *ctx,
@@ -126,7 +133,8 @@ int mbedtls_cipher_cmac_finish( mbedtls_cipher_context_t *ctx,
*
* \param ctx The cipher context used for the CMAC operation.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA
* if parameter verification fails.
*/
int mbedtls_cipher_cmac_reset( mbedtls_cipher_context_t *ctx );
@@ -149,7 +157,8 @@ int mbedtls_cipher_cmac_reset( mbedtls_cipher_context_t *ctx );
* \param ilen The length of the input data.
* \param output The buffer for the generic CMAC result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA
* if parameter verification fails.
*/
int mbedtls_cipher_cmac( const mbedtls_cipher_info_t *cipher_info,
@@ -180,23 +189,12 @@ int mbedtls_aes_cmac_prf_128( const unsigned char *key, size_t key_len,
unsigned char output[16] );
#endif /* MBEDTLS_AES_C */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* !MBEDTLS_CMAC_ALT */
-#include "cmac_alt.h"
-#endif /* !MBEDTLS_CMAC_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#if defined(MBEDTLS_SELF_TEST) && ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C) )
/**
* \brief The CMAC checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_cmac_self_test( int verbose );
#endif /* MBEDTLS_SELF_TEST && ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h
index b5905ef9d0..ae10a4d728 100644
--- a/thirdparty/mbedtls/include/mbedtls/config.h
+++ b/thirdparty/mbedtls/include/mbedtls/config.h
@@ -48,10 +48,14 @@
* Requires support for asm() in compiler.
*
* Used in:
+ * library/aria.c
* library/timing.c
- * library/padlock.c
* include/mbedtls/bn_mul.h
*
+ * Required by:
+ * MBEDTLS_AESNI_C
+ * MBEDTLS_PADLOCK_C
+ *
* Comment to disable the use of assembly code.
*/
#define MBEDTLS_HAVE_ASM
@@ -271,6 +275,7 @@
*/
//#define MBEDTLS_AES_ALT
//#define MBEDTLS_ARC4_ALT
+//#define MBEDTLS_ARIA_ALT
//#define MBEDTLS_BLOWFISH_ALT
//#define MBEDTLS_CAMELLIA_ALT
//#define MBEDTLS_CCM_ALT
@@ -288,6 +293,7 @@
//#define MBEDTLS_SHA256_ALT
//#define MBEDTLS_SHA512_ALT
//#define MBEDTLS_XTEA_ALT
+
/*
* When replacing the elliptic curve module, pleace consider, that it is
* implemented with two .c files:
@@ -440,13 +446,46 @@
/**
* \def MBEDTLS_AES_ROM_TABLES
*
- * Store the AES tables in ROM.
+ * Use precomputed AES tables stored in ROM.
+ *
+ * Uncomment this macro to use precomputed AES tables stored in ROM.
+ * Comment this macro to generate AES tables in RAM at runtime.
+ *
+ * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb
+ * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the
+ * initialization time before the first AES operation can be performed.
+ * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c
+ * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded
+ * performance if ROM access is slower than RAM access.
+ *
+ * This option is independent of \c MBEDTLS_AES_FEWER_TABLES.
*
- * Uncomment this macro to store the AES tables in ROM.
*/
//#define MBEDTLS_AES_ROM_TABLES
/**
+ * \def MBEDTLS_AES_FEWER_TABLES
+ *
+ * Use less ROM/RAM for AES tables.
+ *
+ * Uncommenting this macro omits 75% of the AES tables from
+ * ROM / RAM (depending on the value of \c MBEDTLS_AES_ROM_TABLES)
+ * by computing their values on the fly during operations
+ * (the tables are entry-wise rotations of one another).
+ *
+ * Tradeoff: Uncommenting this reduces the RAM / ROM footprint
+ * by ~6kb but at the cost of more arithmetic operations during
+ * runtime. Specifically, one has to compare 4 accesses within
+ * different tables to 4 accesses with additional arithmetic
+ * operations within the same table. The performance gain/loss
+ * depends on the system and memory details.
+ *
+ * This option is independent of \c MBEDTLS_AES_ROM_TABLES.
+ *
+ */
+//#define MBEDTLS_AES_FEWER_TABLES
+
+/**
* \def MBEDTLS_CAMELLIA_SMALL_MEMORY
*
* Use less ROM for the Camellia implementation (saves about 768 bytes).
@@ -576,6 +615,7 @@
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
+#define MBEDTLS_ECP_DP_CURVE448_ENABLED
/**
* \def MBEDTLS_ECP_NIST_OPTIM
@@ -1583,7 +1623,7 @@
* Enable the AES block cipher.
*
* Module: library/aes.c
- * Caller: library/ssl_tls.c
+ * Caller: library/cipher.c
* library/pem.c
* library/ctr_drbg.c
*
@@ -1658,7 +1698,7 @@
* Enable the ARCFOUR stream cipher.
*
* Module: library/arc4.c
- * Caller: library/ssl_tls.c
+ * Caller: library/cipher.c
*
* This module enables the following ciphersuites (if other requisites are
* enabled as well):
@@ -1752,7 +1792,7 @@
* Enable the Camellia block cipher.
*
* Module: library/camellia.c
- * Caller: library/ssl_tls.c
+ * Caller: library/cipher.c
*
* This module enables the following ciphersuites (if other requisites are
* enabled as well):
@@ -1802,6 +1842,58 @@
#define MBEDTLS_CAMELLIA_C
/**
+ * \def MBEDTLS_ARIA_C
+ *
+ * Enable the ARIA block cipher.
+ *
+ * Module: library/aria.c
+ * Caller: library/cipher.c
+ *
+ * This module enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *
+ * MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384
+ * MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256
+ * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384
+ * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256
+ * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384
+ */
+//#define MBEDTLS_ARIA_C
+
+/**
* \def MBEDTLS_CCM_C
*
* Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher.
@@ -1887,7 +1979,7 @@
*
* Module: library/des.c
* Caller: library/pem.c
- * library/ssl_tls.c
+ * library/cipher.c
*
* This module enables the following ciphersuites (if other requisites are
* enabled as well):
@@ -2818,6 +2910,26 @@
*/
#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
+/**
+ * Uncomment the macro to let mbed TLS use your alternate implementation of
+ * mbedtls_platform_zeroize(). This replaces the default implementation in
+ * platform_util.c.
+ *
+ * mbedtls_platform_zeroize() is a widely used function across the library to
+ * zero a block of memory. The implementation is expected to be secure in the
+ * sense that it has been written to prevent the compiler from removing calls
+ * to mbedtls_platform_zeroize() as part of redundant code elimination
+ * optimizations. However, it is difficult to guarantee that calls to
+ * mbedtls_platform_zeroize() will not be optimized by the compiler as older
+ * versions of the C language standards do not provide a secure implementation
+ * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to
+ * configure their own implementation of mbedtls_platform_zeroize(), for
+ * example by using directives specific to their compiler, features from newer
+ * C standards (e.g using memset_s() in C11) or calling a secure memset() from
+ * their system (e.g explicit_bzero() in BSD).
+ */
+//#define MBEDTLS_PLATFORM_ZEROIZE_ALT
+
/* \} name SECTION: Customisation configuration options */
/* Target and application specific configurations */
diff --git a/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h b/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h
index 121575a51b..dcbc047924 100644
--- a/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h
+++ b/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h
@@ -1,10 +1,15 @@
/**
* \file ctr_drbg.h
*
- * \brief CTR_DRBG is based on AES-256, as defined in <em>NIST SP 800-90A:
- * Recommendation for Random Number Generation Using Deterministic
- * Random Bit Generators</em>.
+ * \brief This file contains CTR_DRBG definitions and functions.
*
+ * CTR_DRBG is a standardized way of building a PRNG from a block-cipher
+ * in counter mode operation, as defined in <em>NIST SP 800-90A:
+ * Recommendation for Random Number Generation Using Deterministic Random
+ * Bit Generators</em>.
+ *
+ * The Mbed TLS implementation of CTR_DRBG uses AES-256 as the underlying
+ * block cipher.
*/
/*
* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved
@@ -156,8 +161,8 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );
identifiers. Can be NULL.
* \param len The length of the personalization data.
*
- * \return \c 0 on success, or
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
*/
int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,
int (*f_entropy)(void *, unsigned char *, size_t),
@@ -216,22 +221,24 @@ void mbedtls_ctr_drbg_set_reseed_interval( mbedtls_ctr_drbg_context *ctx,
* \param additional Additional data to add to the state. Can be NULL.
* \param len The length of the additional data.
*
- * \return \c 0 on success, or
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
*/
int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
const unsigned char *additional, size_t len );
/**
- * \brief This function updates the state of the CTR_DRBG context.
+ * \brief This function updates the state of the CTR_DRBG context.
*
- * \param ctx The CTR_DRBG context.
- * \param additional The data to update the state with.
- * \param add_len Length of \p additional data.
+ * \note If \p add_len is greater than
+ * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT, only the first
+ * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT Bytes are used.
+ * The remaining Bytes are silently discarded.
+ *
+ * \param ctx The CTR_DRBG context.
+ * \param additional The data to update the state with.
+ * \param add_len Length of \p additional data.
*
- * \note If \p add_len is greater than #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT,
- * only the first #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT Bytes are used.
- * The remaining Bytes are silently discarded.
*/
void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx,
const unsigned char *additional, size_t add_len );
@@ -249,8 +256,8 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx,
* \param additional Additional data to update. Can be NULL.
* \param add_len The length of the additional data.
*
- * \return \c 0 on success, or
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
* #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure.
*/
int mbedtls_ctr_drbg_random_with_add( void *p_rng,
@@ -267,8 +274,8 @@ int mbedtls_ctr_drbg_random_with_add( void *p_rng,
* \param output The buffer to fill.
* \param output_len The length of the buffer.
*
- * \return \c 0 on success, or
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
* #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure.
*/
int mbedtls_ctr_drbg_random( void *p_rng,
@@ -281,9 +288,9 @@ int mbedtls_ctr_drbg_random( void *p_rng,
* \param ctx The CTR_DRBG context.
* \param path The name of the file.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error, or
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on
* failure.
*/
int mbedtls_ctr_drbg_write_seed_file( mbedtls_ctr_drbg_context *ctx, const char *path );
@@ -295,9 +302,9 @@ int mbedtls_ctr_drbg_write_seed_file( mbedtls_ctr_drbg_context *ctx, const char
* \param ctx The CTR_DRBG context.
* \param path The name of the file.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error,
- * #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error.
+ * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
* #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG on failure.
*/
int mbedtls_ctr_drbg_update_seed_file( mbedtls_ctr_drbg_context *ctx, const char *path );
@@ -306,7 +313,8 @@ int mbedtls_ctr_drbg_update_seed_file( mbedtls_ctr_drbg_context *ctx, const char
/**
* \brief The CTR_DRBG checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_ctr_drbg_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/des.h b/thirdparty/mbedtls/include/mbedtls/des.h
index 5a1a636522..6eb7d03bae 100644
--- a/thirdparty/mbedtls/include/mbedtls/des.h
+++ b/thirdparty/mbedtls/include/mbedtls/des.h
@@ -46,14 +46,14 @@
#define MBEDTLS_DES_KEY_SIZE 8
-#if !defined(MBEDTLS_DES_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_DES_ALT)
+// Regular implementation
+//
+
/**
* \brief DES context structure
*
@@ -76,6 +76,10 @@ typedef struct
}
mbedtls_des3_context;
+#else /* MBEDTLS_DES_ALT */
+#include "des_alt.h"
+#endif /* MBEDTLS_DES_ALT */
+
/**
* \brief Initialize DES context
*
@@ -331,17 +335,6 @@ int mbedtls_des3_crypt_cbc( mbedtls_des3_context *ctx,
*/
void mbedtls_des_setkey( uint32_t SK[32],
const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_DES_ALT */
-#include "des_alt.h"
-#endif /* MBEDTLS_DES_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
/**
* \brief Checkup routine
diff --git a/thirdparty/mbedtls/include/mbedtls/dhm.h b/thirdparty/mbedtls/include/mbedtls/dhm.h
index 00fafd8d16..75317a8e6d 100644
--- a/thirdparty/mbedtls/include/mbedtls/dhm.h
+++ b/thirdparty/mbedtls/include/mbedtls/dhm.h
@@ -1,7 +1,13 @@
/**
* \file dhm.h
*
- * \brief Diffie-Hellman-Merkle key exchange.
+ * \brief This file contains Diffie-Hellman-Merkle (DHM) key exchange
+ * definitions and functions.
+ *
+ * Diffie-Hellman-Merkle (DHM) key exchange is defined in
+ * <em>RFC-2631: Diffie-Hellman Key Agreement Method</em> and
+ * <em>Public-Key Cryptography Standards (PKCS) #3: Diffie
+ * Hellman Key Agreement Standard</em>.
*
* <em>RFC-3526: More Modular Exponential (MODP) Diffie-Hellman groups for
* Internet Key Exchange (IKE)</em> defines a number of standardized
@@ -65,7 +71,6 @@
#include MBEDTLS_CONFIG_FILE
#endif
#include "bignum.h"
-#if !defined(MBEDTLS_DHM_ALT)
/*
* DHM Error codes
@@ -86,6 +91,8 @@
extern "C" {
#endif
+#if !defined(MBEDTLS_DHM_ALT)
+
/**
* \brief The DHM context structure.
*/
@@ -105,6 +112,10 @@ typedef struct
}
mbedtls_dhm_context;
+#else /* MBEDTLS_DHM_ALT */
+#include "dhm_alt.h"
+#endif /* MBEDTLS_DHM_ALT */
+
/**
* \brief This function initializes the DHM context.
*
@@ -125,8 +136,8 @@ void mbedtls_dhm_init( mbedtls_dhm_context *ctx );
* failures.
* \param end The end of the input buffer.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx,
unsigned char **p,
@@ -136,13 +147,6 @@ int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx,
* \brief This function sets up and writes the ServerKeyExchange
* parameters.
*
- * \param ctx The DHM context.
- * \param x_size The private value size in Bytes.
- * \param olen The number of characters written.
- * \param output The destination buffer.
- * \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
- *
* \note The destination buffer must be large enough to hold
* the reduced binary presentation of the modulus, the generator
* and the public key, each wrapped with a 2-byte length field.
@@ -155,8 +159,15 @@ int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx,
* mbedtls_dhm_set_group() below in conjunction with
* mbedtls_mpi_read_binary() and mbedtls_mpi_read_string().
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
+ * \param ctx The DHM context.
+ * \param x_size The private key size in Bytes.
+ * \param olen The number of characters written.
+ * \param output The destination buffer.
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
unsigned char *output, size_t *olen,
@@ -164,54 +175,54 @@ int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
void *p_rng );
/**
- * \brief Set prime modulus and generator
+ * \brief This function sets the prime modulus and generator.
*
- * \param ctx The DHM context.
- * \param P The MPI holding DHM prime modulus.
- * \param G The MPI holding DHM generator.
+ * \note This function can be used to set \p P, \p G
+ * in preparation for mbedtls_dhm_make_params().
*
- * \note This function can be used to set P, G
- * in preparation for \c mbedtls_dhm_make_params.
+ * \param ctx The DHM context.
+ * \param P The MPI holding the DHM prime modulus.
+ * \param G The MPI holding the DHM generator.
*
- * \return \c 0 if successful, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
+ * \return \c 0 if successful.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_set_group( mbedtls_dhm_context *ctx,
const mbedtls_mpi *P,
const mbedtls_mpi *G );
/**
- * \brief This function imports the public value G^Y of the peer.
+ * \brief This function imports the public value of the peer, G^Y.
*
* \param ctx The DHM context.
- * \param input The input buffer.
+ * \param input The input buffer containing the G^Y value of the peer.
* \param ilen The size of the input buffer.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_read_public( mbedtls_dhm_context *ctx,
const unsigned char *input, size_t ilen );
/**
- * \brief This function creates its own private value \c X and
+ * \brief This function creates its own private key, \c X, and
* exports \c G^X.
*
+ * \note The destination buffer is always fully written
+ * so as to contain a big-endian representation of G^X mod P.
+ * If it is larger than ctx->len, it is padded accordingly
+ * with zero-bytes at the beginning.
+ *
* \param ctx The DHM context.
- * \param x_size The private value size in Bytes.
+ * \param x_size The private key size in Bytes.
* \param output The destination buffer.
* \param olen The length of the destination buffer. Must be at least
- equal to ctx->len (the size of \c P).
+ * equal to ctx->len (the size of \c P).
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \note The destination buffer will always be fully written
- * so as to contain a big-endian presentation of G^X mod P.
- * If it is larger than ctx->len, it will accordingly be
- * padded with zero-bytes in the beginning.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size,
unsigned char *output, size_t olen,
@@ -222,22 +233,22 @@ int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size,
* \brief This function derives and exports the shared secret
* \c (G^Y)^X mod \c P.
*
+ * \note If \p f_rng is not NULL, it is used to blind the input as
+ * a countermeasure against timing attacks. Blinding is used
+ * only if our private key \c X is re-used, and not used
+ * otherwise. We recommend always passing a non-NULL
+ * \p f_rng argument.
+ *
* \param ctx The DHM context.
* \param output The destination buffer.
* \param output_size The size of the destination buffer. Must be at least
- * the size of ctx->len.
+ * the size of ctx->len (the size of \c P).
* \param olen On exit, holds the actual number of Bytes written.
* \param f_rng The RNG function, for blinding purposes.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_DHM_XXX error code
- * on failure.
- *
- * \note If non-NULL, \p f_rng is used to blind the input as
- * a countermeasure against timing attacks. Blinding is used
- * only if our secret value \p X is re-used and omitted
- * otherwise. Therefore, we recommend always passing a
- * non-NULL \p f_rng argument.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure.
*/
int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx,
unsigned char *output, size_t output_size, size_t *olen,
@@ -245,7 +256,7 @@ int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx,
void *p_rng );
/**
- * \brief This function frees and clears the components of a DHM key.
+ * \brief This function frees and clears the components of a DHM context.
*
* \param ctx The DHM context to free and clear.
*/
@@ -261,8 +272,9 @@ void mbedtls_dhm_free( mbedtls_dhm_context *ctx );
* \param dhminlen The size of the buffer, including the terminating null
* Byte for PEM data.
*
- * \return \c 0 on success, or a specific DHM or PEM error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX error code
+ * error code on failure.
*/
int mbedtls_dhm_parse_dhm( mbedtls_dhm_context *dhm, const unsigned char *dhmin,
size_t dhminlen );
@@ -275,29 +287,19 @@ int mbedtls_dhm_parse_dhm( mbedtls_dhm_context *dhm, const unsigned char *dhmin,
* \param dhm The DHM context to load the parameters to.
* \param path The filename to read the DHM parameters from.
*
- * \return \c 0 on success, or a specific DHM or PEM error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX error code
+ * error code on failure.
*/
int mbedtls_dhm_parse_dhmfile( mbedtls_dhm_context *dhm, const char *path );
#endif /* MBEDTLS_FS_IO */
#endif /* MBEDTLS_ASN1_PARSE_C */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_DHM_ALT */
-#include "dhm_alt.h"
-#endif /* MBEDTLS_DHM_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief The DMH checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_dhm_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/ecdh.h b/thirdparty/mbedtls/include/mbedtls/ecdh.h
index 99cfde00d0..5fdf55a88a 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecdh.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecdh.h
@@ -1,10 +1,11 @@
/**
* \file ecdh.h
*
- * \brief The Elliptic Curve Diffie-Hellman (ECDH) protocol APIs.
+ * \brief This file contains ECDH definitions and functions.
*
- * ECDH is an anonymous key agreement protocol allowing two parties to
- * establish a shared secret over an insecure channel. Each party must have an
+ * The Elliptic Curve Diffie-Hellman (ECDH) protocol is an anonymous
+ * key agreement protocol allowing two parties to establish a shared
+ * secret over an insecure channel. Each party must have an
* elliptic-curve public–private key pair.
*
* For more information, see <em>NIST SP 800-56A Rev. 2: Recommendation for
@@ -40,14 +41,12 @@ extern "C" {
#endif
/**
- * Defines the source of the imported EC key:
- * <ul><li>Our key.</li>
- * <li>The key of the peer.</li></ul>
+ * Defines the source of the imported EC key.
*/
typedef enum
{
- MBEDTLS_ECDH_OURS,
- MBEDTLS_ECDH_THEIRS,
+ MBEDTLS_ECDH_OURS, /**< Our key. */
+ MBEDTLS_ECDH_THEIRS, /**< The key of the peer. */
} mbedtls_ecdh_side;
/**
@@ -75,16 +74,18 @@ mbedtls_ecdh_context;
* implemented during the ECDH key exchange. The second core
* computation is performed by mbedtls_ecdh_compute_shared().
*
+ * \see ecp.h
+ *
* \param grp The ECP group.
* \param d The destination MPI (private key).
* \param Q The destination point (public key).
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX or
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or
* \c MBEDTLS_MPI_XXX error code on failure.
*
- * \see ecp.h
*/
int mbedtls_ecdh_gen_public( mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -97,21 +98,22 @@ int mbedtls_ecdh_gen_public( mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp
* implemented during the ECDH key exchange. The first core
* computation is performed by mbedtls_ecdh_gen_public().
*
+ * \see ecp.h
+ *
+ * \note If \p f_rng is not NULL, it is used to implement
+ * countermeasures against side-channel attacks.
+ * For more information, see mbedtls_ecp_mul().
+ *
* \param grp The ECP group.
* \param z The destination MPI (shared secret).
* \param Q The public key from another party.
* \param d Our secret exponent (private key).
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX or
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or
* \c MBEDTLS_MPI_XXX error code on failure.
- *
- * \see ecp.h
- *
- * \note If \p f_rng is not NULL, it is used to implement
- * countermeasures against potential elaborate timing
- * attacks. For more information, see mbedtls_ecp_mul().
*/
int mbedtls_ecdh_compute_shared( mbedtls_ecp_group *grp, mbedtls_mpi *z,
const mbedtls_ecp_point *Q, const mbedtls_mpi *d,
@@ -139,21 +141,21 @@ void mbedtls_ecdh_free( mbedtls_ecdh_context *ctx );
* This is the first function used by a TLS server for ECDHE
* ciphersuites.
*
+ * \note This function assumes that the ECP group (grp) of the
+ * \p ctx context has already been properly set,
+ * for example, using mbedtls_ecp_group_load().
+ *
+ * \see ecp.h
+ *
* \param ctx The ECDH context.
* \param olen The number of characters written.
* \param buf The destination buffer.
* \param blen The length of the destination buffer.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \note This function assumes that the ECP group (grp) of the
- * \p ctx context has already been properly set,
- * for example, using mbedtls_ecp_group_load().
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
- *
- * \see ecp.h
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*/
int mbedtls_ecdh_make_params( mbedtls_ecdh_context *ctx, size_t *olen,
unsigned char *buf, size_t blen,
@@ -167,14 +169,15 @@ int mbedtls_ecdh_make_params( mbedtls_ecdh_context *ctx, size_t *olen,
* This is the first function used by a TLS client for ECDHE
* ciphersuites.
*
+ * \see ecp.h
+ *
* \param ctx The ECDH context.
* \param buf The pointer to the start of the input buffer.
* \param end The address for one Byte past the end of the buffer.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*
- * \see ecp.h
*/
int mbedtls_ecdh_read_params( mbedtls_ecdh_context *ctx,
const unsigned char **buf, const unsigned char *end );
@@ -186,16 +189,16 @@ int mbedtls_ecdh_read_params( mbedtls_ecdh_context *ctx,
* ServerKeyEchange for static ECDH, and imports ECDH
* parameters from the EC key information of a certificate.
*
+ * \see ecp.h
+ *
* \param ctx The ECDH context to set up.
* \param key The EC key to use.
- * \param side Defines the source of the key:
- * <ul><li>1: Our key.</li>
- <li>0: The key of the peer.</li></ul>
+ * \param side Defines the source of the key: 1: Our key, or
+ * 0: The key of the peer.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*
- * \see ecp.h
*/
int mbedtls_ecdh_get_params( mbedtls_ecdh_context *ctx, const mbedtls_ecp_keypair *key,
mbedtls_ecdh_side side );
@@ -207,17 +210,17 @@ int mbedtls_ecdh_get_params( mbedtls_ecdh_context *ctx, const mbedtls_ecp_keypai
* This is the second function used by a TLS client for ECDH(E)
* ciphersuites.
*
+ * \see ecp.h
+ *
* \param ctx The ECDH context.
* \param olen The number of Bytes written.
* \param buf The destination buffer.
* \param blen The size of the destination buffer.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
+ * \param p_rng The RNG context.
*
- * \see ecp.h
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*/
int mbedtls_ecdh_make_public( mbedtls_ecdh_context *ctx, size_t *olen,
unsigned char *buf, size_t blen,
@@ -231,14 +234,14 @@ int mbedtls_ecdh_make_public( mbedtls_ecdh_context *ctx, size_t *olen,
* This is the second function used by a TLS server for ECDH(E)
* ciphersuites.
*
+ * \see ecp.h
+ *
* \param ctx The ECDH context.
* \param buf The start of the input buffer.
* \param blen The length of the input buffer.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
- *
- * \see ecp.h
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*/
int mbedtls_ecdh_read_public( mbedtls_ecdh_context *ctx,
const unsigned char *buf, size_t blen );
@@ -249,21 +252,21 @@ int mbedtls_ecdh_read_public( mbedtls_ecdh_context *ctx,
* This is the last function used by both TLS client
* and servers.
*
+ * \note If \p f_rng is not NULL, it is used to implement
+ * countermeasures against side-channel attacks.
+ * For more information, see mbedtls_ecp_mul().
+ *
+ * \see ecp.h
+ *
* \param ctx The ECDH context.
* \param olen The number of Bytes written.
* \param buf The destination buffer.
* \param blen The length of the destination buffer.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX error code
- * on failure.
+ * \param p_rng The RNG context.
*
- * \see ecp.h
- *
- * \note If \p f_rng is not NULL, it is used to implement
- * countermeasures against potential elaborate timing
- * attacks. For more information, see mbedtls_ecp_mul().
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure.
*/
int mbedtls_ecdh_calc_secret( mbedtls_ecdh_context *ctx, size_t *olen,
unsigned char *buf, size_t blen,
diff --git a/thirdparty/mbedtls/include/mbedtls/ecdsa.h b/thirdparty/mbedtls/include/mbedtls/ecdsa.h
index aa23d67f99..ce1a03d791 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecdsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecdsa.h
@@ -1,9 +1,10 @@
/**
* \file ecdsa.h
*
- * \brief The Elliptic Curve Digital Signature Algorithm (ECDSA).
+ * \brief This file contains ECDSA definitions and functions.
*
- * ECDSA is defined in <em>Standards for Efficient Cryptography Group (SECG):
+ * The Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in
+ * <em>Standards for Efficient Cryptography Group (SECG):
* SEC1 Elliptic Curve Cryptography</em>.
* The use of ECDSA for TLS is defined in <em>RFC-4492: Elliptic Curve
* Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)</em>.
@@ -69,6 +70,14 @@ extern "C" {
*
* \note The deterministic version is usually preferred.
*
+ * \note If the bitlength of the message hash is larger than the
+ * bitlength of the group order, then the hash is truncated
+ * as defined in <em>Standards for Efficient Cryptography Group
+ * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
+ * 4.1.3, step 5.
+ *
+ * \see ecp.h
+ *
* \param grp The ECP group.
* \param r The first output integer.
* \param s The second output integer.
@@ -76,18 +85,11 @@ extern "C" {
* \param buf The message hash.
* \param blen The length of \p buf.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
*
- * \note If the bitlength of the message hash is larger than the
- * bitlength of the group order, then the hash is truncated
- * as defined in <em>Standards for Efficient Cryptography Group
- * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
- * 4.1.3, step 5.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX
* or \c MBEDTLS_MPI_XXX error code on failure.
- *
- * \see ecp.h
*/
int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
@@ -97,10 +99,19 @@ int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
/**
* \brief This function computes the ECDSA signature of a
* previously-hashed message, deterministic version.
+ *
* For more information, see <em>RFC-6979: Deterministic
* Usage of the Digital Signature Algorithm (DSA) and Elliptic
* Curve Digital Signature Algorithm (ECDSA)</em>.
*
+ * \note If the bitlength of the message hash is larger than the
+ * bitlength of the group order, then the hash is truncated as
+ * defined in <em>Standards for Efficient Cryptography Group
+ * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
+ * 4.1.3, step 5.
+ *
+ * \see ecp.h
+ *
* \param grp The ECP group.
* \param r The first output integer.
* \param s The second output integer.
@@ -109,17 +120,9 @@ int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
* \param blen The length of \p buf.
* \param md_alg The MD algorithm used to hash the message.
*
- * \note If the bitlength of the message hash is larger than the
- * bitlength of the group order, then the hash is truncated as
- * defined in <em>Standards for Efficient Cryptography Group
- * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
- * 4.1.3, step 5.
- *
- * \return \c 0 on success,
- * or an \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
* error code on failure.
- *
- * \see ecp.h
*/
int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
@@ -130,6 +133,14 @@ int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi
* \brief This function verifies the ECDSA signature of a
* previously-hashed message.
*
+ * \note If the bitlength of the message hash is larger than the
+ * bitlength of the group order, then the hash is truncated as
+ * defined in <em>Standards for Efficient Cryptography Group
+ * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
+ * 4.1.4, step 3.
+ *
+ * \see ecp.h
+ *
* \param grp The ECP group.
* \param buf The message hash.
* \param blen The length of \p buf.
@@ -137,18 +148,11 @@ int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi
* \param r The first integer of the signature.
* \param s The second integer of the signature.
*
- * \note If the bitlength of the message hash is larger than the
- * bitlength of the group order, then the hash is truncated as
- * defined in <em>Standards for Efficient Cryptography Group
- * (SECG): SEC1 Elliptic Curve Cryptography</em>, section
- * 4.1.4, step 3.
- *
- * \return \c 0 on success,
- * #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid,
- * or an \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature
+ * is invalid.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
* error code on failure for any other reason.
- *
- * \see ecp.h
*/
int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
const unsigned char *buf, size_t blen,
@@ -169,15 +173,6 @@ int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
* of the Digital Signature Algorithm (DSA) and Elliptic
* Curve Digital Signature Algorithm (ECDSA)</em>.
*
- * \param ctx The ECDSA context.
- * \param md_alg The message digest that was used to hash the message.
- * \param hash The message hash.
- * \param hlen The length of the hash.
- * \param sig The buffer that holds the signature.
- * \param slen The length of the signature written.
- * \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
- *
* \note The \p sig buffer must be at least twice as large as the
* size of the curve used, plus 9. For example, 73 Bytes if
* a 256-bit curve is used. A buffer length of
@@ -189,11 +184,20 @@ int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
* (SECG): SEC1 Elliptic Curve Cryptography</em>, section
* 4.1.3, step 5.
*
- * \return \c 0 on success,
- * or an \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
- * \c MBEDTLS_ERR_ASN1_XXX error code on failure.
- *
* \see ecp.h
+ *
+ * \param ctx The ECDSA context.
+ * \param md_alg The message digest that was used to hash the message.
+ * \param hash The message hash.
+ * \param hlen The length of the hash.
+ * \param sig The buffer that holds the signature.
+ * \param slen The length of the signature written.
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
+ * \c MBEDTLS_ERR_ASN1_XXX error code on failure.
*/
int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, mbedtls_md_type_t md_alg,
const unsigned char *hash, size_t hlen,
@@ -209,26 +213,17 @@ int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, mbedtls_md_type_t
#define MBEDTLS_DEPRECATED
#endif
/**
- * \brief This function computes an ECDSA signature and writes it to a buffer,
- * serialized as defined in <em>RFC-4492: Elliptic Curve Cryptography
- * (ECC) Cipher Suites for Transport Layer Security (TLS)</em>.
+ * \brief This function computes an ECDSA signature and writes
+ * it to a buffer, serialized as defined in <em>RFC-4492:
+ * Elliptic Curve Cryptography (ECC) Cipher Suites for
+ * Transport Layer Security (TLS)</em>.
*
- * The deterministic version is defined in <em>RFC-6979:
- * Deterministic Usage of the Digital Signature Algorithm (DSA) and
- * Elliptic Curve Digital Signature Algorithm (ECDSA)</em>.
+ * The deterministic version is defined in <em>RFC-6979:
+ * Deterministic Usage of the Digital Signature Algorithm (DSA)
+ * and Elliptic Curve Digital Signature Algorithm (ECDSA)</em>.
*
* \warning It is not thread-safe to use the same context in
* multiple threads.
-
- *
- * \deprecated Superseded by mbedtls_ecdsa_write_signature() in 2.0.0
- *
- * \param ctx The ECDSA context.
- * \param hash The Message hash.
- * \param hlen The length of the hash.
- * \param sig The buffer that holds the signature.
- * \param slen The length of the signature written.
- * \param md_alg The MD algorithm used to hash the message.
*
* \note The \p sig buffer must be at least twice as large as the
* size of the curve used, plus 9. For example, 73 Bytes if a
@@ -241,11 +236,21 @@ int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, mbedtls_md_type_t
* (SECG): SEC1 Elliptic Curve Cryptography</em>, section
* 4.1.3, step 5.
*
- * \return \c 0 on success,
- * or an \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
- * \c MBEDTLS_ERR_ASN1_XXX error code on failure.
- *
* \see ecp.h
+ *
+ * \deprecated Superseded by mbedtls_ecdsa_write_signature() in
+ * Mbed TLS version 2.0 and later.
+ *
+ * \param ctx The ECDSA context.
+ * \param hash The message hash.
+ * \param hlen The length of the hash.
+ * \param sig The buffer that holds the signature.
+ * \param slen The length of the signature written.
+ * \param md_alg The MD algorithm used to hash the message.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
+ * \c MBEDTLS_ERR_ASN1_XXX error code on failure.
*/
int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx,
const unsigned char *hash, size_t hlen,
@@ -258,26 +263,26 @@ int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx,
/**
* \brief This function reads and verifies an ECDSA signature.
*
- * \param ctx The ECDSA context.
- * \param hash The message hash.
- * \param hlen The size of the hash.
- * \param sig The signature to read and verify.
- * \param slen The size of \p sig.
- *
* \note If the bitlength of the message hash is larger than the
* bitlength of the group order, then the hash is truncated as
* defined in <em>Standards for Efficient Cryptography Group
* (SECG): SEC1 Elliptic Curve Cryptography</em>, section
* 4.1.4, step 3.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid,
- * #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if the signature is
- * valid but its actual length is less than \p siglen,
- * or an \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX
- * error code on failure for any other reason.
- *
* \see ecp.h
+ *
+ * \param ctx The ECDSA context.
+ * \param hash The message hash.
+ * \param hlen The size of the hash.
+ * \param sig The signature to read and verify.
+ * \param slen The size of \p sig.
+ *
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid.
+ * \return #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if there is a valid
+ * signature in \p sig, but its length is less than \p siglen.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX
+ * error code on failure for any other reason.
*/
int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx,
const unsigned char *hash, size_t hlen,
@@ -286,16 +291,16 @@ int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx,
/**
* \brief This function generates an ECDSA keypair on the given curve.
*
+ * \see ecp.h
+ *
* \param ctx The ECDSA context to store the keypair in.
* \param gid The elliptic curve to use. One of the various
* \c MBEDTLS_ECP_DP_XXX macros depending on configuration.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX code on
- * failure.
+ * \param p_rng The RNG context.
*
- * \see ecp.h
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX code on failure.
*/
int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
@@ -303,13 +308,13 @@ int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
/**
* \brief This function sets an ECDSA context from an EC key pair.
*
+ * \see ecp.h
+ *
* \param ctx The ECDSA context to set.
* \param key The EC key to use.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_ECP_XXX code on
- * failure.
- *
- * \see ecp.h
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX code on failure.
*/
int mbedtls_ecdsa_from_keypair( mbedtls_ecdsa_context *ctx, const mbedtls_ecp_keypair *key );
diff --git a/thirdparty/mbedtls/include/mbedtls/ecjpake.h b/thirdparty/mbedtls/include/mbedtls/ecjpake.h
index d86e8207f1..cc2b316f5e 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecjpake.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecjpake.h
@@ -44,8 +44,6 @@
#include "ecp.h"
#include "md.h"
-#if !defined(MBEDTLS_ECJPAKE_ALT)
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -58,6 +56,7 @@ typedef enum {
MBEDTLS_ECJPAKE_SERVER, /**< Server */
} mbedtls_ecjpake_role;
+#if !defined(MBEDTLS_ECJPAKE_ALT)
/**
* EC J-PAKE context structure.
*
@@ -88,6 +87,10 @@ typedef struct
mbedtls_mpi s; /**< Pre-shared secret (passphrase) */
} mbedtls_ecjpake_context;
+#else /* MBEDTLS_ECJPAKE_ALT */
+#include "ecjpake_alt.h"
+#endif /* MBEDTLS_ECJPAKE_ALT */
+
/**
* \brief Initialize a context
* (just makes it ready for setup() or free()).
@@ -225,20 +228,10 @@ int mbedtls_ecjpake_derive_secret( mbedtls_ecjpake_context *ctx,
*/
void mbedtls_ecjpake_free( mbedtls_ecjpake_context *ctx );
-#ifdef __cplusplus
-}
-#endif
-#else /* MBEDTLS_ECJPAKE_ALT */
-#include "ecjpake_alt.h"
-#endif /* MBEDTLS_ECJPAKE_ALT */
#if defined(MBEDTLS_SELF_TEST)
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Checkup routine
*
@@ -246,10 +239,11 @@ extern "C" {
*/
int mbedtls_ecjpake_self_test( int verbose );
+#endif /* MBEDTLS_SELF_TEST */
+
#ifdef __cplusplus
}
#endif
-#endif /* MBEDTLS_SELF_TEST */
#endif /* ecjpake.h */
diff --git a/thirdparty/mbedtls/include/mbedtls/ecp.h b/thirdparty/mbedtls/include/mbedtls/ecp.h
index b00ba4da87..3a407986dd 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecp.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecp.h
@@ -1,10 +1,21 @@
/**
* \file ecp.h
*
- * \brief Elliptic curves over GF(p)
+ * \brief This file provides an API for Elliptic Curves over GF(P) (ECP).
+ *
+ * The use of ECP in cryptography and TLS is defined in
+ * <em>Standards for Efficient Cryptography Group (SECG): SEC1
+ * Elliptic Curve Cryptography</em> and
+ * <em>RFC-4492: Elliptic Curve Cryptography (ECC) Cipher Suites
+ * for Transport Layer Security (TLS)</em>.
+ *
+ * <em>RFC-2409: The Internet Key Exchange (IKE)</em> defines ECP
+ * group types.
+ *
*/
+
/*
- * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ * Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,8 +30,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
- * This file is part of mbed TLS (https://tls.mbed.org)
+ * This file is part of Mbed TLS (https://tls.mbed.org)
*/
+
#ifndef MBEDTLS_ECP_H
#define MBEDTLS_ECP_H
@@ -31,160 +43,157 @@
*/
#define MBEDTLS_ERR_ECP_BAD_INPUT_DATA -0x4F80 /**< Bad input parameters to function. */
#define MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL -0x4F00 /**< The buffer is too small to write to. */
-#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE -0x4E80 /**< Requested curve not available. */
+#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE -0x4E80 /**< The requested feature is not available, for example, the requested curve is not supported. */
#define MBEDTLS_ERR_ECP_VERIFY_FAILED -0x4E00 /**< The signature is not valid. */
#define MBEDTLS_ERR_ECP_ALLOC_FAILED -0x4D80 /**< Memory allocation failed. */
-#define MBEDTLS_ERR_ECP_RANDOM_FAILED -0x4D00 /**< Generation of random value, such as (ephemeral) key, failed. */
+#define MBEDTLS_ERR_ECP_RANDOM_FAILED -0x4D00 /**< Generation of random value, such as ephemeral key, failed. */
#define MBEDTLS_ERR_ECP_INVALID_KEY -0x4C80 /**< Invalid private or public key. */
-#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH -0x4C00 /**< Signature is valid but shorter than the user-supplied length. */
-#define MBEDTLS_ERR_ECP_HW_ACCEL_FAILED -0x4B80 /**< ECP hardware accelerator failed. */
-
-#if !defined(MBEDTLS_ECP_ALT)
-/*
- * default mbed TLS elliptic curve arithmetic implementation
- *
- * (in case MBEDTLS_ECP_ALT is defined then the developer has to provide an
- * alternative implementation for the whole module and it will replace this
- * one.)
- */
+#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH -0x4C00 /**< The buffer contains a valid signature followed by more data. */
+#define MBEDTLS_ERR_ECP_HW_ACCEL_FAILED -0x4B80 /**< The ECP hardware accelerator failed. */
#ifdef __cplusplus
extern "C" {
#endif
/**
- * Domain parameters (curve, subgroup and generator) identifiers.
+ * Domain-parameter identifiers: curve, subgroup, and generator.
*
- * Only curves over prime fields are supported.
+ * \note Only curves over prime fields are supported.
*
* \warning This library does not support validation of arbitrary domain
- * parameters. Therefore, only well-known domain parameters from trusted
+ * parameters. Therefore, only standardized domain parameters from trusted
* sources should be used. See mbedtls_ecp_group_load().
*/
typedef enum
{
- MBEDTLS_ECP_DP_NONE = 0,
- MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */
- MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */
- MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */
- MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */
- MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */
- MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */
- MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */
- MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */
- MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */
- MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */
- MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
- MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
+ MBEDTLS_ECP_DP_NONE = 0, /*!< Curve not defined. */
+ MBEDTLS_ECP_DP_SECP192R1, /*!< Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1. */
+ MBEDTLS_ECP_DP_SECP224R1, /*!< Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1. */
+ MBEDTLS_ECP_DP_SECP256R1, /*!< Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1. */
+ MBEDTLS_ECP_DP_SECP384R1, /*!< Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1. */
+ MBEDTLS_ECP_DP_SECP521R1, /*!< Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1. */
+ MBEDTLS_ECP_DP_BP256R1, /*!< Domain parameters for 256-bit Brainpool curve. */
+ MBEDTLS_ECP_DP_BP384R1, /*!< Domain parameters for 384-bit Brainpool curve. */
+ MBEDTLS_ECP_DP_BP512R1, /*!< Domain parameters for 512-bit Brainpool curve. */
+ MBEDTLS_ECP_DP_CURVE25519, /*!< Domain parameters for Curve25519. */
+ MBEDTLS_ECP_DP_SECP192K1, /*!< Domain parameters for 192-bit "Koblitz" curve. */
+ MBEDTLS_ECP_DP_SECP224K1, /*!< Domain parameters for 224-bit "Koblitz" curve. */
+ MBEDTLS_ECP_DP_SECP256K1, /*!< Domain parameters for 256-bit "Koblitz" curve. */
+ MBEDTLS_ECP_DP_CURVE448, /*!< Domain parameters for Curve448. */
} mbedtls_ecp_group_id;
/**
- * Number of supported curves (plus one for NONE).
+ * The number of supported curves, plus one for #MBEDTLS_ECP_DP_NONE.
*
- * (Montgomery curves excluded for now.)
+ * \note Montgomery curves are currently excluded.
*/
#define MBEDTLS_ECP_DP_MAX 12
/**
- * Curve information for use by other modules
+ * Curve information, for use by other modules.
*/
typedef struct
{
- mbedtls_ecp_group_id grp_id; /*!< Internal identifier */
- uint16_t tls_id; /*!< TLS NamedCurve identifier */
- uint16_t bit_size; /*!< Curve size in bits */
- const char *name; /*!< Human-friendly name */
+ mbedtls_ecp_group_id grp_id; /*!< An internal identifier. */
+ uint16_t tls_id; /*!< The TLS NamedCurve identifier. */
+ uint16_t bit_size; /*!< The curve size in bits. */
+ const char *name; /*!< A human-friendly name. */
} mbedtls_ecp_curve_info;
/**
- * \brief ECP point structure (jacobian coordinates)
+ * \brief The ECP point structure, in Jacobian coordinates.
*
* \note All functions expect and return points satisfying
- * the following condition: Z == 0 or Z == 1. (Other
- * values of Z are used by internal functions only.)
- * The point is zero, or "at infinity", if Z == 0.
- * Otherwise, X and Y are its standard (affine) coordinates.
+ * the following condition: <code>Z == 0</code> or
+ * <code>Z == 1</code>. Other values of \p Z are
+ * used only by internal functions.
+ * The point is zero, or "at infinity", if <code>Z == 0</code>.
+ * Otherwise, \p X and \p Y are its standard (affine)
+ * coordinates.
*/
typedef struct
{
- mbedtls_mpi X; /*!< the point's X coordinate */
- mbedtls_mpi Y; /*!< the point's Y coordinate */
- mbedtls_mpi Z; /*!< the point's Z coordinate */
+ mbedtls_mpi X; /*!< The X coordinate of the ECP point. */
+ mbedtls_mpi Y; /*!< The Y coordinate of the ECP point. */
+ mbedtls_mpi Z; /*!< The Z coordinate of the ECP point. */
}
mbedtls_ecp_point;
-/**
- * \brief ECP group structure
- *
- * We consider two types of curves equations:
- * 1. Short Weierstrass y^2 = x^3 + A x + B mod P (SEC1 + RFC 4492)
- * 2. Montgomery, y^2 = x^3 + A x^2 + x mod P (Curve25519 + draft)
- * In both cases, a generator G for a prime-order subgroup is fixed. In the
- * short weierstrass, this subgroup is actually the whole curve, and its
- * cardinal is denoted by N.
- *
- * In the case of Short Weierstrass curves, our code requires that N is an odd
- * prime. (Use odd in mbedtls_ecp_mul() and prime in mbedtls_ecdsa_sign() for blinding.)
- *
- * In the case of Montgomery curves, we don't store A but (A + 2) / 4 which is
- * the quantity actually used in the formulas. Also, nbits is not the size of N
- * but the required size for private keys.
+#if !defined(MBEDTLS_ECP_ALT)
+/*
+ * default mbed TLS elliptic curve arithmetic implementation
*
- * If modp is NULL, reduction modulo P is done using a generic algorithm.
- * Otherwise, it must point to a function that takes an mbedtls_mpi in the range
- * 0..2^(2*pbits)-1 and transforms it in-place in an integer of little more
- * than pbits, so that the integer may be efficiently brought in the 0..P-1
- * range by a few additions or substractions. It must return 0 on success and
- * non-zero on failure.
+ * (in case MBEDTLS_ECP_ALT is defined then the developer has to provide an
+ * alternative implementation for the whole module and it will replace this
+ * one.)
*/
-typedef struct
-{
- mbedtls_ecp_group_id id; /*!< internal group identifier */
- mbedtls_mpi P; /*!< prime modulus of the base field */
- mbedtls_mpi A; /*!< 1. A in the equation, or 2. (A + 2) / 4 */
- mbedtls_mpi B; /*!< 1. B in the equation, or 2. unused */
- mbedtls_ecp_point G; /*!< generator of the (sub)group used */
- mbedtls_mpi N; /*!< 1. the order of G, or 2. unused */
- size_t pbits; /*!< number of bits in P */
- size_t nbits; /*!< number of bits in 1. P, or 2. private keys */
- unsigned int h; /*!< internal: 1 if the constants are static */
- int (*modp)(mbedtls_mpi *); /*!< function for fast reduction mod P */
- int (*t_pre)(mbedtls_ecp_point *, void *); /*!< unused */
- int (*t_post)(mbedtls_ecp_point *, void *); /*!< unused */
- void *t_data; /*!< unused */
- mbedtls_ecp_point *T; /*!< pre-computed points for ecp_mul_comb() */
- size_t T_size; /*!< number for pre-computed points */
-}
-mbedtls_ecp_group;
/**
- * \brief ECP key pair structure
+ * \brief The ECP group structure.
+ *
+ * We consider two types of curve equations:
+ * <ul><li>Short Weierstrass: <code>y^2 = x^3 + A x + B mod P</code>
+ * (SEC1 + RFC-4492)</li>
+ * <li>Montgomery: <code>y^2 = x^3 + A x^2 + x mod P</code> (Curve25519,
+ * Curve448)</li></ul>
+ * In both cases, the generator (\p G) for a prime-order subgroup is fixed.
*
- * A generic key pair that could be used for ECDSA, fixed ECDH, etc.
+ * For Short Weierstrass, this subgroup is the whole curve, and its
+ * cardinality is denoted by \p N. Our code requires that \p N is an
+ * odd prime as mbedtls_ecp_mul() requires an odd number, and
+ * mbedtls_ecdsa_sign() requires that it is prime for blinding purposes.
+ *
+ * For Montgomery curves, we do not store \p A, but <code>(A + 2) / 4</code>,
+ * which is the quantity used in the formulas. Additionally, \p nbits is
+ * not the size of \p N but the required size for private keys.
+ *
+ * If \p modp is NULL, reduction modulo \p P is done using a generic algorithm.
+ * Otherwise, \p modp must point to a function that takes an \p mbedtls_mpi in the
+ * range of <code>0..2^(2*pbits)-1</code>, and transforms it in-place to an integer
+ * which is congruent mod \p P to the given MPI, and is close enough to \p pbits
+ * in size, so that it may be efficiently brought in the 0..P-1 range by a few
+ * additions or subtractions. Therefore, it is only an approximative modular
+ * reduction. It must return 0 on success and non-zero on failure.
*
- * \note Members purposefully in the same order as struc mbedtls_ecdsa_context.
*/
typedef struct
{
- mbedtls_ecp_group grp; /*!< Elliptic curve and base point */
- mbedtls_mpi d; /*!< our secret value */
- mbedtls_ecp_point Q; /*!< our public value */
+ mbedtls_ecp_group_id id; /*!< An internal group identifier. */
+ mbedtls_mpi P; /*!< The prime modulus of the base field. */
+ mbedtls_mpi A; /*!< For Short Weierstrass: \p A in the equation. For
+ Montgomery curves: <code>(A + 2) / 4</code>. */
+ mbedtls_mpi B; /*!< For Short Weierstrass: \p B in the equation.
+ For Montgomery curves: unused. */
+ mbedtls_ecp_point G; /*!< The generator of the subgroup used. */
+ mbedtls_mpi N; /*!< The order of \p G. */
+ size_t pbits; /*!< The number of bits in \p P.*/
+ size_t nbits; /*!< For Short Weierstrass: The number of bits in \p P.
+ For Montgomery curves: the number of bits in the
+ private keys. */
+ unsigned int h; /*!< \internal 1 if the constants are static. */
+ int (*modp)(mbedtls_mpi *); /*!< The function for fast pseudo-reduction
+ mod \p P (see above).*/
+ int (*t_pre)(mbedtls_ecp_point *, void *); /*!< Unused. */
+ int (*t_post)(mbedtls_ecp_point *, void *); /*!< Unused. */
+ void *t_data; /*!< Unused. */
+ mbedtls_ecp_point *T; /*!< Pre-computed points for ecp_mul_comb(). */
+ size_t T_size; /*!< The number of pre-computed points. */
}
-mbedtls_ecp_keypair;
+mbedtls_ecp_group;
/**
* \name SECTION: Module settings
*
* The configuration options you can set for this module are in this section.
- * Either change them in config.h or define them on the compiler command line.
+ * Either change them in config.h, or define them using the compiler command line.
* \{
*/
#if !defined(MBEDTLS_ECP_MAX_BITS)
/**
- * Maximum size of the groups (that is, of N and P)
+ * The maximum size of the groups, that is, of \c N and \c P.
*/
-#define MBEDTLS_ECP_MAX_BITS 521 /**< Maximum bit size of groups */
+#define MBEDTLS_ECP_MAX_BITS 521 /**< The maximum size of groups, in bits. */
#endif
#define MBEDTLS_ECP_MAX_BYTES ( ( MBEDTLS_ECP_MAX_BITS + 7 ) / 8 )
@@ -207,11 +216,10 @@ mbedtls_ecp_keypair;
* 521 145 141 135 120 97
* 384 214 209 198 177 146
* 256 320 320 303 262 226
-
* 224 475 475 453 398 342
* 192 640 640 633 587 476
*/
-#define MBEDTLS_ECP_WINDOW_SIZE 6 /**< Maximum window size used */
+#define MBEDTLS_ECP_WINDOW_SIZE 6 /**< The maximum window size used. */
#endif /* MBEDTLS_ECP_WINDOW_SIZE */
#if !defined(MBEDTLS_ECP_FIXED_POINT_OPTIM)
@@ -226,33 +234,55 @@ mbedtls_ecp_keypair;
*
* Change this value to 0 to reduce peak memory usage.
*/
-#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */
+#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up. */
#endif /* MBEDTLS_ECP_FIXED_POINT_OPTIM */
/* \} name SECTION: Module settings */
+#else /* MBEDTLS_ECP_ALT */
+#include "ecp_alt.h"
+#endif /* MBEDTLS_ECP_ALT */
+
+/**
+ * \brief The ECP key-pair structure.
+ *
+ * A generic key-pair that may be used for ECDSA and fixed ECDH, for example.
+ *
+ * \note Members are deliberately in the same order as in the
+ * ::mbedtls_ecdsa_context structure.
+ */
+typedef struct
+{
+ mbedtls_ecp_group grp; /*!< Elliptic curve and base point */
+ mbedtls_mpi d; /*!< our secret value */
+ mbedtls_ecp_point Q; /*!< our public value */
+}
+mbedtls_ecp_keypair;
+
/*
* Point formats, from RFC 4492's enum ECPointFormat
*/
-#define MBEDTLS_ECP_PF_UNCOMPRESSED 0 /**< Uncompressed point format */
-#define MBEDTLS_ECP_PF_COMPRESSED 1 /**< Compressed point format */
+#define MBEDTLS_ECP_PF_UNCOMPRESSED 0 /**< Uncompressed point format. */
+#define MBEDTLS_ECP_PF_COMPRESSED 1 /**< Compressed point format. */
/*
* Some other constants from RFC 4492
*/
-#define MBEDTLS_ECP_TLS_NAMED_CURVE 3 /**< ECCurveType's named_curve */
+#define MBEDTLS_ECP_TLS_NAMED_CURVE 3 /**< The named_curve of ECCurveType. */
/**
- * \brief Get the list of supported curves in order of preferrence
- * (full information)
+ * \brief This function retrieves the information defined in
+ * mbedtls_ecp_curve_info() for all supported curves in order
+ * of preference.
*
- * \return A statically allocated array, the last entry is 0.
+ * \return A statically allocated array. The last entry is 0.
*/
const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list( void );
/**
- * \brief Get the list of supported curves in order of preferrence
- * (grp_id only)
+ * \brief This function retrieves the list of internal group
+ * identifiers of all supported curves in the order of
+ * preference.
*
* \return A statically allocated array,
* terminated with MBEDTLS_ECP_DP_NONE.
@@ -260,357 +290,400 @@ const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list( void );
const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list( void );
/**
- * \brief Get curve information from an internal group identifier
+ * \brief This function retrieves curve information from an internal
+ * group identifier.
*
- * \param grp_id A MBEDTLS_ECP_DP_XXX value
+ * \param grp_id An \c MBEDTLS_ECP_DP_XXX value.
*
- * \return The associated curve information or NULL
+ * \return The associated curve information on success.
+ * \return NULL on failure.
*/
const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id( mbedtls_ecp_group_id grp_id );
/**
- * \brief Get curve information from a TLS NamedCurve value
+ * \brief This function retrieves curve information from a TLS
+ * NamedCurve value.
*
- * \param tls_id A MBEDTLS_ECP_DP_XXX value
+ * \param tls_id An \c MBEDTLS_ECP_DP_XXX value.
*
- * \return The associated curve information or NULL
+ * \return The associated curve information on success.
+ * \return NULL on failure.
*/
const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id( uint16_t tls_id );
/**
- * \brief Get curve information from a human-readable name
+ * \brief This function retrieves curve information from a
+ * human-readable name.
*
- * \param name The name
+ * \param name The human-readable name.
*
- * \return The associated curve information or NULL
+ * \return The associated curve information on success.
+ * \return NULL on failure.
*/
const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name( const char *name );
/**
- * \brief Initialize a point (as zero)
+ * \brief This function initializes a point as zero.
+ *
+ * \param pt The point to initialize.
*/
void mbedtls_ecp_point_init( mbedtls_ecp_point *pt );
/**
- * \brief Initialize a group (to something meaningless)
+ * \brief This function initializes an ECP group context
+ * without loading any domain parameters.
+ *
+ * \note After this function is called, domain parameters
+ * for various ECP groups can be loaded through the
+ * mbedtls_ecp_load() or mbedtls_ecp_tls_read_group()
+ * functions.
*/
void mbedtls_ecp_group_init( mbedtls_ecp_group *grp );
/**
- * \brief Initialize a key pair (as an invalid one)
+ * \brief This function initializes a key pair as an invalid one.
+ *
+ * \param key The key pair to initialize.
*/
void mbedtls_ecp_keypair_init( mbedtls_ecp_keypair *key );
/**
- * \brief Free the components of a point
+ * \brief This function frees the components of a point.
+ *
+ * \param pt The point to free.
*/
void mbedtls_ecp_point_free( mbedtls_ecp_point *pt );
/**
- * \brief Free the components of an ECP group
+ * \brief This function frees the components of an ECP group.
+ * \param grp The group to free.
*/
void mbedtls_ecp_group_free( mbedtls_ecp_group *grp );
/**
- * \brief Free the components of a key pair
+ * \brief This function frees the components of a key pair.
+ * \param key The key pair to free.
*/
void mbedtls_ecp_keypair_free( mbedtls_ecp_keypair *key );
/**
- * \brief Copy the contents of point Q into P
+ * \brief This function copies the contents of point \p Q into
+ * point \p P.
*
- * \param P Destination point
- * \param Q Source point
+ * \param P The destination point.
+ * \param Q The source point.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_ecp_copy( mbedtls_ecp_point *P, const mbedtls_ecp_point *Q );
/**
- * \brief Copy the contents of a group object
+ * \brief This function copies the contents of group \p src into
+ * group \p dst.
*
- * \param dst Destination group
- * \param src Source group
+ * \param dst The destination group.
+ * \param src The source group.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_ecp_group_copy( mbedtls_ecp_group *dst, const mbedtls_ecp_group *src );
/**
- * \brief Set a point to zero
+ * \brief This function sets a point to zero.
*
- * \param pt Destination point
+ * \param pt The point to set.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_ecp_set_zero( mbedtls_ecp_point *pt );
/**
- * \brief Tell if a point is zero
+ * \brief This function checks if a point is zero.
*
- * \param pt Point to test
+ * \param pt The point to test.
*
- * \return 1 if point is zero, 0 otherwise
+ * \return \c 1 if the point is zero.
+ * \return \c 0 if the point is non-zero.
*/
int mbedtls_ecp_is_zero( mbedtls_ecp_point *pt );
/**
- * \brief Compare two points
+ * \brief This function compares two points.
*
- * \note This assumes the points are normalized. Otherwise,
+ * \note This assumes that the points are normalized. Otherwise,
* they may compare as "not equal" even if they are.
*
- * \param P First point to compare
- * \param Q Second point to compare
+ * \param P The first point to compare.
+ * \param Q The second point to compare.
*
- * \return 0 if the points are equal,
- * MBEDTLS_ERR_ECP_BAD_INPUT_DATA otherwise
+ * \return \c 0 if the points are equal.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the points are not equal.
*/
int mbedtls_ecp_point_cmp( const mbedtls_ecp_point *P,
const mbedtls_ecp_point *Q );
/**
- * \brief Import a non-zero point from two ASCII strings
+ * \brief This function imports a non-zero point from two ASCII
+ * strings.
*
- * \param P Destination point
- * \param radix Input numeric base
- * \param x First affine coordinate as a null-terminated string
- * \param y Second affine coordinate as a null-terminated string
+ * \param P The destination point.
+ * \param radix The numeric base of the input.
+ * \param x The first affine coordinate, as a null-terminated string.
+ * \param y The second affine coordinate, as a null-terminated string.
*
- * \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_MPI_XXX error code on failure.
*/
int mbedtls_ecp_point_read_string( mbedtls_ecp_point *P, int radix,
const char *x, const char *y );
/**
- * \brief Export a point into unsigned binary data
+ * \brief This function exports a point into unsigned binary data.
*
- * \param grp Group to which the point should belong
- * \param P Point to export
- * \param format Point format, should be a MBEDTLS_ECP_PF_XXX macro
- * \param olen Length of the actual output
- * \param buf Output buffer
- * \param buflen Length of the output buffer
+ * \param grp The group to which the point should belong.
+ * \param P The point to export.
+ * \param format The point format. Should be an \c MBEDTLS_ECP_PF_XXX macro.
+ * \param olen The length of the output.
+ * \param buf The output buffer.
+ * \param buflen The length of the output buffer.
*
- * \return 0 if successful,
- * or MBEDTLS_ERR_ECP_BAD_INPUT_DATA
- * or MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+ * or #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL on failure.
*/
int mbedtls_ecp_point_write_binary( const mbedtls_ecp_group *grp, const mbedtls_ecp_point *P,
int format, size_t *olen,
unsigned char *buf, size_t buflen );
/**
- * \brief Import a point from unsigned binary data
+ * \brief This function imports a point from unsigned binary data.
*
- * \param grp Group to which the point should belong
- * \param P Point to import
- * \param buf Input buffer
- * \param ilen Actual length of input
+ * \note This function does not check that the point actually
+ * belongs to the given group, see mbedtls_ecp_check_pubkey()
+ * for that.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
- * MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the point format
+ * \param grp The group to which the point should belong.
+ * \param P The point to import.
+ * \param buf The input buffer.
+ * \param ilen The length of the input.
+ *
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
+ * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the point format
* is not implemented.
*
- * \note This function does NOT check that the point actually
- * belongs to the given group, see mbedtls_ecp_check_pubkey() for
- * that.
*/
int mbedtls_ecp_point_read_binary( const mbedtls_ecp_group *grp, mbedtls_ecp_point *P,
const unsigned char *buf, size_t ilen );
/**
- * \brief Import a point from a TLS ECPoint record
+ * \brief This function imports a point from a TLS ECPoint record.
*
- * \param grp ECP group used
- * \param pt Destination point
- * \param buf $(Start of input buffer)
- * \param len Buffer length
+ * \note On function return, \p buf is updated to point to immediately
+ * after the ECPoint record.
*
- * \note buf is updated to point right after the ECPoint on exit
+ * \param grp The ECP group used.
+ * \param pt The destination point.
+ * \param buf The address of the pointer to the start of the input buffer.
+ * \param len The length of the buffer.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_XXX if initialization failed
- * MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_MPI_XXX error code on initialization failure.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid.
*/
int mbedtls_ecp_tls_read_point( const mbedtls_ecp_group *grp, mbedtls_ecp_point *pt,
const unsigned char **buf, size_t len );
/**
- * \brief Export a point as a TLS ECPoint record
+ * \brief This function exports a point as a TLS ECPoint record.
*
- * \param grp ECP group used
- * \param pt Point to export
- * \param format Export format
- * \param olen length of data written
- * \param buf Buffer to write to
- * \param blen Buffer length
+ * \param grp The ECP group used.
+ * \param pt The point format to export to. The point format is an
+ * \c MBEDTLS_ECP_PF_XXX constant.
+ * \param format The export format.
+ * \param olen The length of the data written.
+ * \param buf The buffer to write to.
+ * \param blen The length of the buffer.
*
- * \return 0 if successful,
- * or MBEDTLS_ERR_ECP_BAD_INPUT_DATA
- * or MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA or
+ * #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL on failure.
*/
int mbedtls_ecp_tls_write_point( const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt,
int format, size_t *olen,
unsigned char *buf, size_t blen );
/**
- * \brief Set a group using well-known domain parameters
+ * \brief This function sets a group using standardized domain parameters.
*
- * \param grp Destination group
- * \param id Index in the list of well-known domain parameters
+ * \note The index should be a value of the NamedCurve enum,
+ * as defined in <em>RFC-4492: Elliptic Curve Cryptography
+ * (ECC) Cipher Suites for Transport Layer Security (TLS)</em>,
+ * usually in the form of an \c MBEDTLS_ECP_DP_XXX macro.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_XXX if initialization failed
- * MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE for unkownn groups
+ * \param grp The destination group.
+ * \param id The identifier of the domain parameter set to load.
*
- * \note Index should be a value of RFC 4492's enum NamedCurve,
- * usually in the form of a MBEDTLS_ECP_DP_XXX macro.
+ * \return \c 0 on success,
+ * \return An \c MBEDTLS_ERR_MPI_XXX error code on initialization failure.
+ * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE for unkownn groups.
+
*/
int mbedtls_ecp_group_load( mbedtls_ecp_group *grp, mbedtls_ecp_group_id id );
/**
- * \brief Set a group from a TLS ECParameters record
+ * \brief This function sets a group from a TLS ECParameters record.
*
- * \param grp Destination group
- * \param buf &(Start of input buffer)
- * \param len Buffer length
+ * \note \p buf is updated to point right after the ECParameters record
+ * on exit.
*
- * \note buf is updated to point right after ECParameters on exit
+ * \param grp The destination group.
+ * \param buf The address of the pointer to the start of the input buffer.
+ * \param len The length of the buffer.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_MPI_XXX if initialization failed
- * MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_MPI_XXX error code on initialization failure.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid.
*/
int mbedtls_ecp_tls_read_group( mbedtls_ecp_group *grp, const unsigned char **buf, size_t len );
/**
- * \brief Write the TLS ECParameters record for a group
+ * \brief This function writes the TLS ECParameters record for a group.
*
- * \param grp ECP group used
- * \param olen Number of bytes actually written
- * \param buf Buffer to write to
- * \param blen Buffer length
+ * \param grp The ECP group used.
+ * \param olen The number of Bytes written.
+ * \param buf The buffer to write to.
+ * \param blen The length of the buffer.
*
- * \return 0 if successful,
- * or MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL on failure.
*/
int mbedtls_ecp_tls_write_group( const mbedtls_ecp_group *grp, size_t *olen,
unsigned char *buf, size_t blen );
/**
- * \brief Multiplication by an integer: R = m * P
- * (Not thread-safe to use same group in multiple threads)
+ * \brief This function performs multiplication of a point by
+ * an integer: \p R = \p m * \p P.
*
- * \note In order to prevent timing attacks, this function
- * executes the exact same sequence of (base field)
- * operations for any valid m. It avoids any if-branch or
- * array index depending on the value of m.
+ * It is not thread-safe to use same group in multiple threads.
*
- * \note If f_rng is not NULL, it is used to randomize intermediate
- * results in order to prevent potential timing attacks
- * targeting these results. It is recommended to always
- * provide a non-NULL f_rng (the overhead is negligible).
+ * \note To prevent timing attacks, this function
+ * executes the exact same sequence of base-field
+ * operations for any valid \p m. It avoids any if-branch or
+ * array index depending on the value of \p m.
*
- * \param grp ECP group
- * \param R Destination point
- * \param m Integer by which to multiply
- * \param P Point to multiply
- * \param f_rng RNG function (see notes)
- * \param p_rng RNG parameter
+ * \note If \p f_rng is not NULL, it is used to randomize
+ * intermediate results to prevent potential timing attacks
+ * targeting these results. We recommend always providing
+ * a non-NULL \p f_rng. The overhead is negligible.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_ECP_INVALID_KEY if m is not a valid privkey
- * or P is not a valid pubkey,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * \param grp The ECP group.
+ * \param R The destination point.
+ * \param m The integer by which to multiply.
+ * \param P The point to multiply.
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
+ *
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m is not a valid private
+ * key, or \p P is not a valid public key.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_ecp_mul( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
const mbedtls_mpi *m, const mbedtls_ecp_point *P,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
/**
- * \brief Multiplication and addition of two points by integers:
- * R = m * P + n * Q
- * (Not thread-safe to use same group in multiple threads)
+ * \brief This function performs multiplication and addition of two
+ * points by integers: \p R = \p m * \p P + \p n * \p Q
+ *
+ * It is not thread-safe to use same group in multiple threads.
*
- * \note In contrast to mbedtls_ecp_mul(), this function does not guarantee
- * a constant execution flow and timing.
+ * \note In contrast to mbedtls_ecp_mul(), this function does not
+ * guarantee a constant execution flow and timing.
*
- * \param grp ECP group
- * \param R Destination point
- * \param m Integer by which to multiply P
- * \param P Point to multiply by m
- * \param n Integer by which to multiply Q
- * \param Q Point to be multiplied by n
+ * \param grp The ECP group.
+ * \param R The destination point.
+ * \param m The integer by which to multiply \p P.
+ * \param P The point to multiply by \p m.
+ * \param n The integer by which to multiply \p Q.
+ * \param Q The point to be multiplied by \p n.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_ECP_INVALID_KEY if m or n is not a valid privkey
- * or P or Q is not a valid pubkey,
- * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m or \p n are not
+ * valid private keys, or \p P or \p Q are not valid public
+ * keys.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
const mbedtls_mpi *m, const mbedtls_ecp_point *P,
const mbedtls_mpi *n, const mbedtls_ecp_point *Q );
/**
- * \brief Check that a point is a valid public key on this curve
+ * \brief This function checks that a point is a valid public key
+ * on this curve.
*
- * \param grp Curve/group the point should belong to
- * \param pt Point to check
+ * It only checks that the point is non-zero, has
+ * valid coordinates and lies on the curve. It does not verify
+ * that it is indeed a multiple of \p G. This additional
+ * check is computationally more expensive, is not required
+ * by standards, and should not be necessary if the group
+ * used has a small cofactor. In particular, it is useless for
+ * the NIST groups which all have a cofactor of 1.
*
- * \return 0 if point is a valid public key,
- * MBEDTLS_ERR_ECP_INVALID_KEY otherwise.
+ * \note This function uses bare components rather than an
+ * ::mbedtls_ecp_keypair structure, to ease use with other
+ * structures, such as ::mbedtls_ecdh_context or
+ * ::mbedtls_ecdsa_context.
*
- * \note This function only checks the point is non-zero, has valid
- * coordinates and lies on the curve, but not that it is
- * indeed a multiple of G. This is additional check is more
- * expensive, isn't required by standards, and shouldn't be
- * necessary if the group used has a small cofactor. In
- * particular, it is useless for the NIST groups which all
- * have a cofactor of 1.
+ * \param grp The curve the point should lie on.
+ * \param pt The point to check.
*
- * \note Uses bare components rather than an mbedtls_ecp_keypair structure
- * in order to ease use with other structures such as
- * mbedtls_ecdh_context of mbedtls_ecdsa_context.
+ * \return \c 0 if the point is a valid public key.
+ * \return #MBEDTLS_ERR_ECP_INVALID_KEY on failure.
*/
int mbedtls_ecp_check_pubkey( const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt );
/**
- * \brief Check that an mbedtls_mpi is a valid private key for this curve
+ * \brief This function checks that an \p mbedtls_mpi is a valid private
+ * key for this curve.
*
- * \param grp Group used
- * \param d Integer to check
+ * \note This function uses bare components rather than an
+ * ::mbedtls_ecp_keypair structure to ease use with other
+ * structures, such as ::mbedtls_ecdh_context or
+ * ::mbedtls_ecdsa_context.
*
- * \return 0 if point is a valid private key,
- * MBEDTLS_ERR_ECP_INVALID_KEY otherwise.
+ * \param grp The group used.
+ * \param d The integer to check.
*
- * \note Uses bare components rather than an mbedtls_ecp_keypair structure
- * in order to ease use with other structures such as
- * mbedtls_ecdh_context of mbedtls_ecdsa_context.
+ * \return \c 0 if the point is a valid private key.
+ * \return #MBEDTLS_ERR_ECP_INVALID_KEY on failure.
*/
int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, const mbedtls_mpi *d );
/**
- * \brief Generate a keypair with configurable base point
+ * \brief This function generates a keypair with a configurable base
+ * point.
*
- * \param grp ECP group
- * \param G Chosen base point
- * \param d Destination MPI (secret part)
- * \param Q Destination point (public part)
- * \param f_rng RNG function
- * \param p_rng RNG parameter
+ * \note This function uses bare components rather than an
+ * ::mbedtls_ecp_keypair structure to ease use with other
+ * structures, such as ::mbedtls_ecdh_context or
+ * ::mbedtls_ecdsa_context.
*
- * \return 0 if successful,
- * or a MBEDTLS_ERR_ECP_XXX or MBEDTLS_MPI_XXX error code
+ * \param grp The ECP group.
+ * \param G The chosen base point.
+ * \param d The destination MPI (secret part).
+ * \param Q The destination point (public part).
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
*
- * \note Uses bare components rather than an mbedtls_ecp_keypair structure
- * in order to ease use with other structures such as
- * mbedtls_ecdh_context of mbedtls_ecdsa_context.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
+ * on failure.
*/
int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
const mbedtls_ecp_point *G,
@@ -619,57 +692,66 @@ int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
void *p_rng );
/**
- * \brief Generate a keypair
+ * \brief This function generates an ECP keypair.
*
- * \param grp ECP group
- * \param d Destination MPI (secret part)
- * \param Q Destination point (public part)
- * \param f_rng RNG function
- * \param p_rng RNG parameter
+ * \note This function uses bare components rather than an
+ * ::mbedtls_ecp_keypair structure to ease use with other
+ * structures, such as ::mbedtls_ecdh_context or
+ * ::mbedtls_ecdsa_context.
*
- * \return 0 if successful,
- * or a MBEDTLS_ERR_ECP_XXX or MBEDTLS_MPI_XXX error code
+ * \param grp The ECP group.
+ * \param d The destination MPI (secret part).
+ * \param Q The destination point (public part).
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
*
- * \note Uses bare components rather than an mbedtls_ecp_keypair structure
- * in order to ease use with other structures such as
- * mbedtls_ecdh_context of mbedtls_ecdsa_context.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
+ * on failure.
*/
int mbedtls_ecp_gen_keypair( mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
- * \brief Generate a keypair
+ * \brief This function generates an ECP key.
*
- * \param grp_id ECP group identifier
- * \param key Destination keypair
- * \param f_rng RNG function
- * \param p_rng RNG parameter
+ * \param grp_id The ECP group identifier.
+ * \param key The destination key.
+ * \param f_rng The RNG function.
+ * \param p_rng The RNG context.
*
- * \return 0 if successful,
- * or a MBEDTLS_ERR_ECP_XXX or MBEDTLS_MPI_XXX error code
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
+ * on failure.
*/
int mbedtls_ecp_gen_key( mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
/**
- * \brief Check a public-private key pair
+ * \brief This function checks that the keypair objects
+ * \p pub and \p prv have the same group and the
+ * same public point, and that the private key in
+ * \p prv is consistent with the public key.
*
- * \param pub Keypair structure holding a public key
- * \param prv Keypair structure holding a private (plus public) key
+ * \param pub The keypair structure holding the public key.
+ * If it contains a private key, that part is ignored.
+ * \param prv The keypair structure holding the full keypair.
*
- * \return 0 if successful (keys are valid and match), or
- * MBEDTLS_ERR_ECP_BAD_INPUT_DATA, or
- * a MBEDTLS_ERR_ECP_XXX or MBEDTLS_ERR_MPI_XXX code.
+ * \return \c 0 on success, meaning that the keys are valid and match.
+ * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the keys are invalid or do not match.
+ * \return An \c MBEDTLS_ERR_ECP_XXX or an \c MBEDTLS_ERR_MPI_XXX
+ * error code on calculation failure.
*/
int mbedtls_ecp_check_pub_priv( const mbedtls_ecp_keypair *pub, const mbedtls_ecp_keypair *prv );
#if defined(MBEDTLS_SELF_TEST)
/**
- * \brief Checkup routine
+ * \brief The ECP checkup routine.
*
- * \return 0 if successful, or 1 if a test failed
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_ecp_self_test( int verbose );
@@ -679,8 +761,4 @@ int mbedtls_ecp_self_test( int verbose );
}
#endif
-#else /* MBEDTLS_ECP_ALT */
-#include "ecp_alt.h"
-#endif /* MBEDTLS_ECP_ALT */
-
#endif /* ecp.h */
diff --git a/thirdparty/mbedtls/include/mbedtls/ecp_internal.h b/thirdparty/mbedtls/include/mbedtls/ecp_internal.h
index 8a6d517ed0..18040697ad 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecp_internal.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecp_internal.h
@@ -48,7 +48,7 @@
* [6] Digital Signature Standard (DSS), FIPS 186-4.
* <http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf>
*
- * [7] Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer
+ * [7] Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer
* Security (TLS), RFC 4492.
* <https://tools.ietf.org/search/rfc4492>
*
diff --git a/thirdparty/mbedtls/include/mbedtls/entropy.h b/thirdparty/mbedtls/include/mbedtls/entropy.h
index fcb4d02557..a5cb05a584 100644
--- a/thirdparty/mbedtls/include/mbedtls/entropy.h
+++ b/thirdparty/mbedtls/include/mbedtls/entropy.h
@@ -166,7 +166,7 @@ void mbedtls_entropy_free( mbedtls_entropy_context *ctx );
* \param threshold Minimum required from source before entropy is released
* ( with mbedtls_entropy_func() ) (in bytes)
* \param strong MBEDTLS_ENTROPY_SOURCE_STRONG or
- * MBEDTSL_ENTROPY_SOURCE_WEAK.
+ * MBEDTLS_ENTROPY_SOURCE_WEAK.
* At least one strong source needs to be added.
* Weaker sources (such as the cycle counter) can be used as
* a complement.
diff --git a/thirdparty/mbedtls/include/mbedtls/error.h b/thirdparty/mbedtls/include/mbedtls/error.h
index 8b4d3a8755..a17f8d8ace 100644
--- a/thirdparty/mbedtls/include/mbedtls/error.h
+++ b/thirdparty/mbedtls/include/mbedtls/error.h
@@ -53,7 +53,7 @@
* GCM 3 0x0012-0x0014 0x0013-0x0013
* BLOWFISH 3 0x0016-0x0018 0x0017-0x0017
* THREADING 3 0x001A-0x001E
- * AES 4 0x0020-0x0022 0x0023-0x0025
+ * AES 5 0x0020-0x0022 0x0021-0x0025
* CAMELLIA 3 0x0024-0x0026 0x0027-0x0027
* XTEA 2 0x0028-0x0028 0x0029-0x0029
* BASE64 2 0x002A-0x002C
@@ -63,6 +63,7 @@
* CTR_DBRG 4 0x0034-0x003A
* ENTROPY 3 0x003C-0x0040 0x003D-0x003F
* NET 11 0x0042-0x0052 0x0043-0x0045
+ * ARIA 4 0x0058-0x005E
* ASN1 7 0x0060-0x006C
* CMAC 1 0x007A-0x007A
* PBKDF2 1 0x007C-0x007C
diff --git a/thirdparty/mbedtls/include/mbedtls/gcm.h b/thirdparty/mbedtls/include/mbedtls/gcm.h
index 1e5a507a26..bec5577142 100644
--- a/thirdparty/mbedtls/include/mbedtls/gcm.h
+++ b/thirdparty/mbedtls/include/mbedtls/gcm.h
@@ -1,9 +1,11 @@
/**
* \file gcm.h
*
- * \brief Galois/Counter Mode (GCM) for 128-bit block ciphers, as defined
- * in <em>D. McGrew, J. Viega, The Galois/Counter Mode of Operation
- * (GCM), Natl. Inst. Stand. Technol.</em>
+ * \brief This file contains GCM definitions and functions.
+ *
+ * The Galois/Counter Mode (GCM) for 128-bit block ciphers is defined
+ * in <em>D. McGrew, J. Viega, The Galois/Counter Mode of Operation
+ * (GCM), Natl. Inst. Stand. Technol.</em>
*
* For more information on GCM, see <em>NIST SP 800-38D: Recommendation for
* Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC</em>.
@@ -42,12 +44,12 @@
#define MBEDTLS_ERR_GCM_HW_ACCEL_FAILED -0x0013 /**< GCM hardware accelerator failed. */
#define MBEDTLS_ERR_GCM_BAD_INPUT -0x0014 /**< Bad input parameters to function. */
-#if !defined(MBEDTLS_GCM_ALT)
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_GCM_ALT)
+
/**
* \brief The GCM context structure.
*/
@@ -66,6 +68,10 @@ typedef struct {
}
mbedtls_gcm_context;
+#else /* !MBEDTLS_GCM_ALT */
+#include "gcm_alt.h"
+#endif /* !MBEDTLS_GCM_ALT */
+
/**
* \brief This function initializes the specified GCM context,
* to make references valid, and prepares the context
@@ -91,7 +97,8 @@ void mbedtls_gcm_init( mbedtls_gcm_context *ctx );
* <li>192 bits</li>
* <li>256 bits</li></ul>
*
- * \return \c 0 on success, or a cipher specific error code.
+ * \return \c 0 on success.
+ * \return A cipher-specific error code on failure.
*/
int mbedtls_gcm_setkey( mbedtls_gcm_context *ctx,
mbedtls_cipher_id_t cipher,
@@ -101,15 +108,16 @@ int mbedtls_gcm_setkey( mbedtls_gcm_context *ctx,
/**
* \brief This function performs GCM encryption or decryption of a buffer.
*
- * \note For encryption, the output buffer can be the same as the input buffer.
- * For decryption, the output buffer cannot be the same as input buffer.
- * If the buffers overlap, the output buffer must trail at least 8 Bytes
- * behind the input buffer.
+ * \note For encryption, the output buffer can be the same as the
+ * input buffer. For decryption, the output buffer cannot be
+ * the same as input buffer. If the buffers overlap, the output
+ * buffer must trail at least 8 Bytes behind the input buffer.
*
* \param ctx The GCM context to use for encryption or decryption.
* \param mode The operation to perform: #MBEDTLS_GCM_ENCRYPT or
* #MBEDTLS_GCM_DECRYPT.
- * \param length The length of the input data. This must be a multiple of 16 except in the last call before mbedtls_gcm_finish().
+ * \param length The length of the input data. This must be a multiple of
+ * 16 except in the last call before mbedtls_gcm_finish().
* \param iv The initialization vector.
* \param iv_len The length of the IV.
* \param add The buffer holding the additional data.
@@ -137,12 +145,13 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx,
* \brief This function performs a GCM authenticated decryption of a
* buffer.
*
- * \note For decryption, the output buffer cannot be the same as input buffer.
- * If the buffers overlap, the output buffer must trail at least 8 Bytes
- * behind the input buffer.
+ * \note For decryption, the output buffer cannot be the same as
+ * input buffer. If the buffers overlap, the output buffer
+ * must trail at least 8 Bytes behind the input buffer.
*
* \param ctx The GCM context.
- * \param length The length of the input data. This must be a multiple of 16 except in the last call before mbedtls_gcm_finish().
+ * \param length The length of the input data. This must be a multiple
+ * of 16 except in the last call before mbedtls_gcm_finish().
* \param iv The initialization vector.
* \param iv_len The length of the IV.
* \param add The buffer holding the additional data.
@@ -152,8 +161,8 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx,
* \param input The buffer holding the input data.
* \param output The buffer for holding the output data.
*
- * \return 0 if successful and authenticated, or
- * #MBEDTLS_ERR_GCM_AUTH_FAILED if tag does not match.
+ * \return 0 if successful and authenticated.
+ * \return #MBEDTLS_ERR_GCM_AUTH_FAILED if the tag does not match.
*/
int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx,
size_t length,
@@ -175,10 +184,12 @@ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx,
* #MBEDTLS_GCM_DECRYPT.
* \param iv The initialization vector.
* \param iv_len The length of the IV.
- * \param add The buffer holding the additional data, or NULL if \p add_len is 0.
- * \param add_len The length of the additional data. If 0, \p add is NULL.
+ * \param add The buffer holding the additional data, or NULL
+ * if \p add_len is 0.
+ * \param add_len The length of the additional data. If 0,
+ * \p add is NULL.
*
- * \return \c 0 on success.
+ * \return \c 0 on success.
*/
int mbedtls_gcm_starts( mbedtls_gcm_context *ctx,
int mode,
@@ -195,16 +206,18 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx,
* Bytes. Only the last call before calling
* mbedtls_gcm_finish() can be less than 16 Bytes.
*
- * \note For decryption, the output buffer cannot be the same as input buffer.
- * If the buffers overlap, the output buffer must trail at least 8 Bytes
- * behind the input buffer.
+ * \note For decryption, the output buffer cannot be the same as
+ * input buffer. If the buffers overlap, the output buffer
+ * must trail at least 8 Bytes behind the input buffer.
*
* \param ctx The GCM context.
- * \param length The length of the input data. This must be a multiple of 16 except in the last call before mbedtls_gcm_finish().
+ * \param length The length of the input data. This must be a multiple of
+ * 16 except in the last call before mbedtls_gcm_finish().
* \param input The buffer holding the input data.
* \param output The buffer for holding the output data.
*
- * \return \c 0 on success, or #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
*/
int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
size_t length,
@@ -222,7 +235,8 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
* \param tag The buffer for holding the tag.
* \param tag_len The length of the tag to generate. Must be at least four.
*
- * \return \c 0 on success, or #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
*/
int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
unsigned char *tag,
@@ -236,22 +250,11 @@ int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
*/
void mbedtls_gcm_free( mbedtls_gcm_context *ctx );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* !MBEDTLS_GCM_ALT */
-#include "gcm_alt.h"
-#endif /* !MBEDTLS_GCM_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief The GCM checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_gcm_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/md.h b/thirdparty/mbedtls/include/mbedtls/md.h
index 06538c3827..6b6f5c53dd 100644
--- a/thirdparty/mbedtls/include/mbedtls/md.h
+++ b/thirdparty/mbedtls/include/mbedtls/md.h
@@ -1,7 +1,7 @@
/**
* \file md.h
*
- * \brief The generic message-digest wrapper.
+ * \brief This file contains the generic message-digest wrapper.
*
* \author Adriaan de Jong <dejong@fox-it.com>
*/
@@ -46,7 +46,7 @@ extern "C" {
#endif
/**
- * \brief Enumeration of supported message digests
+ * \brief Supported message digests.
*
* \warning MD2, MD4, MD5 and SHA-1 are considered weak message digests and
* their use constitutes a security risk. We recommend considering
@@ -54,16 +54,16 @@ extern "C" {
*
*/
typedef enum {
- MBEDTLS_MD_NONE=0,
- MBEDTLS_MD_MD2,
- MBEDTLS_MD_MD4,
- MBEDTLS_MD_MD5,
- MBEDTLS_MD_SHA1,
- MBEDTLS_MD_SHA224,
- MBEDTLS_MD_SHA256,
- MBEDTLS_MD_SHA384,
- MBEDTLS_MD_SHA512,
- MBEDTLS_MD_RIPEMD160,
+ MBEDTLS_MD_NONE=0, /**< None. */
+ MBEDTLS_MD_MD2, /**< The MD2 message digest. */
+ MBEDTLS_MD_MD4, /**< The MD4 message digest. */
+ MBEDTLS_MD_MD5, /**< The MD5 message digest. */
+ MBEDTLS_MD_SHA1, /**< The SHA-1 message digest. */
+ MBEDTLS_MD_SHA224, /**< The SHA-224 message digest. */
+ MBEDTLS_MD_SHA256, /**< The SHA-256 message digest. */
+ MBEDTLS_MD_SHA384, /**< The SHA-384 message digest. */
+ MBEDTLS_MD_SHA512, /**< The SHA-512 message digest. */
+ MBEDTLS_MD_RIPEMD160, /**< The RIPEMD-160 message digest. */
} mbedtls_md_type_t;
#if defined(MBEDTLS_SHA512_C)
@@ -108,8 +108,8 @@ const int *mbedtls_md_list( void );
*
* \param md_name The name of the digest to search for.
*
- * \return The message-digest information associated with \p md_name,
- * or NULL if not found.
+ * \return The message-digest information associated with \p md_name.
+ * \return NULL if the associated message-digest information is not found.
*/
const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name );
@@ -119,8 +119,8 @@ const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name );
*
* \param md_type The type of digest to search for.
*
- * \return The message-digest information associated with \p md_type,
- * or NULL if not found.
+ * \return The message-digest information associated with \p md_type.
+ * \return NULL if the associated message-digest information is not found.
*/
const mbedtls_md_info_t *mbedtls_md_info_from_type( mbedtls_md_type_t md_type );
@@ -168,9 +168,10 @@ void mbedtls_md_free( mbedtls_md_context_t *ctx );
* \param md_info The information structure of the message-digest algorithm
* to use.
*
- * \returns \c 0 on success,
- * #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter failure,
- * #MBEDTLS_ERR_MD_ALLOC_FAILED memory allocation failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
+ * \return #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_md_init_ctx( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info ) MBEDTLS_DEPRECATED;
#undef MBEDTLS_DEPRECATED
@@ -187,12 +188,13 @@ int mbedtls_md_init_ctx( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_
* \param ctx The context to set up.
* \param md_info The information structure of the message-digest algorithm
* to use.
- * \param hmac <ul><li>0: HMAC is not used. Saves some memory.</li>
- * <li>non-zero: HMAC is used with this context.</li></ul>
+ * \param hmac Defines if HMAC is used. 0: HMAC is not used (saves some memory),
+ * or non-zero: HMAC is used with this context.
*
- * \returns \c 0 on success,
- * #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter failure, or
- * #MBEDTLS_ERR_MD_ALLOC_FAILED on memory allocation failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
+ * \return #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure.
*/
int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac );
@@ -212,8 +214,8 @@ int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_inf
* \param dst The destination context.
* \param src The context to be cloned.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification failure.
*/
int mbedtls_md_clone( mbedtls_md_context_t *dst,
const mbedtls_md_context_t *src );
@@ -260,8 +262,9 @@ const char *mbedtls_md_get_name( const mbedtls_md_info_t *md_info );
*
* \param ctx The generic message-digest context.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_starts( mbedtls_md_context_t *ctx );
@@ -277,8 +280,9 @@ int mbedtls_md_starts( mbedtls_md_context_t *ctx );
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
*
- * \returns \c 0 on success, #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_update( mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen );
@@ -296,8 +300,9 @@ int mbedtls_md_update( mbedtls_md_context_t *ctx, const unsigned char *input, si
* \param ctx The generic message-digest context.
* \param output The buffer for the generic message-digest checksum result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_finish( mbedtls_md_context_t *ctx, unsigned char *output );
@@ -315,8 +320,9 @@ int mbedtls_md_finish( mbedtls_md_context_t *ctx, unsigned char *output );
* \param ilen The length of the input data.
* \param output The generic message-digest checksum result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md( const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen,
unsigned char *output );
@@ -334,9 +340,10 @@ int mbedtls_md( const mbedtls_md_info_t *md_info, const unsigned char *input, si
* \param path The input file name.
* \param output The generic message-digest checksum result.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_MD_FILE_IO_ERROR if file input failed, or
- * #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info was NULL.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_FILE_IO_ERROR on an I/O error accessing
+ * the file pointed by \p path.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info was NULL.
*/
int mbedtls_md_file( const mbedtls_md_info_t *md_info, const char *path,
unsigned char *output );
@@ -356,8 +363,9 @@ int mbedtls_md_file( const mbedtls_md_info_t *md_info, const char *path,
* \param key The HMAC secret key.
* \param keylen The length of the HMAC key in Bytes.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key,
size_t keylen );
@@ -377,8 +385,9 @@ int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key,
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *input,
size_t ilen );
@@ -397,8 +406,9 @@ int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *inpu
* context.
* \param output The generic HMAC checksum result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output);
@@ -413,8 +423,9 @@ int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output);
* \param ctx The message digest context containing an embedded HMAC
* context.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_hmac_reset( mbedtls_md_context_t *ctx );
@@ -436,8 +447,9 @@ int mbedtls_md_hmac_reset( mbedtls_md_context_t *ctx );
* \param ilen The length of the input data.
* \param output The generic HMAC result.
*
- * \returns \c 0 on success, or #MBEDTLS_ERR_MD_BAD_INPUT_DATA if
- * parameter verification fails.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
+ * failure.
*/
int mbedtls_md_hmac( const mbedtls_md_info_t *md_info, const unsigned char *key, size_t keylen,
const unsigned char *input, size_t ilen,
diff --git a/thirdparty/mbedtls/include/mbedtls/md2.h b/thirdparty/mbedtls/include/mbedtls/md2.h
index 0fd8b5afcc..08e75b247b 100644
--- a/thirdparty/mbedtls/include/mbedtls/md2.h
+++ b/thirdparty/mbedtls/include/mbedtls/md2.h
@@ -39,14 +39,14 @@
#define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED -0x002B /**< MD2 hardware accelerator failed */
-#if !defined(MBEDTLS_MD2_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_MD2_ALT)
+// Regular implementation
+//
+
/**
* \brief MD2 context structure
*
@@ -64,6 +64,10 @@ typedef struct
}
mbedtls_md2_context;
+#else /* MBEDTLS_MD2_ALT */
+#include "md2_alt.h"
+#endif /* MBEDTLS_MD2_ALT */
+
/**
* \brief Initialize MD2 context
*
@@ -235,18 +239,6 @@ MBEDTLS_DEPRECATED void mbedtls_md2_process( mbedtls_md2_context *ctx );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_MD2_ALT */
-#include "md2_alt.h"
-#endif /* MBEDTLS_MD2_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Output = MD2( input buffer )
*
diff --git a/thirdparty/mbedtls/include/mbedtls/md4.h b/thirdparty/mbedtls/include/mbedtls/md4.h
index 23fa95e46a..8ee4e5cabf 100644
--- a/thirdparty/mbedtls/include/mbedtls/md4.h
+++ b/thirdparty/mbedtls/include/mbedtls/md4.h
@@ -40,14 +40,14 @@
#define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED -0x002D /**< MD4 hardware accelerator failed */
-#if !defined(MBEDTLS_MD4_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_MD4_ALT)
+// Regular implementation
+//
+
/**
* \brief MD4 context structure
*
@@ -64,6 +64,10 @@ typedef struct
}
mbedtls_md4_context;
+#else /* MBEDTLS_MD4_ALT */
+#include "md4_alt.h"
+#endif /* MBEDTLS_MD4_ALT */
+
/**
* \brief Initialize MD4 context
*
@@ -238,18 +242,6 @@ MBEDTLS_DEPRECATED void mbedtls_md4_process( mbedtls_md4_context *ctx,
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_MD4_ALT */
-#include "md4_alt.h"
-#endif /* MBEDTLS_MD4_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Output = MD4( input buffer )
*
diff --git a/thirdparty/mbedtls/include/mbedtls/md5.h b/thirdparty/mbedtls/include/mbedtls/md5.h
index 06ea4c5d44..43ead4b747 100644
--- a/thirdparty/mbedtls/include/mbedtls/md5.h
+++ b/thirdparty/mbedtls/include/mbedtls/md5.h
@@ -39,14 +39,14 @@
#define MBEDTLS_ERR_MD5_HW_ACCEL_FAILED -0x002F /**< MD5 hardware accelerator failed */
-#if !defined(MBEDTLS_MD5_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_MD5_ALT)
+// Regular implementation
+//
+
/**
* \brief MD5 context structure
*
@@ -63,6 +63,10 @@ typedef struct
}
mbedtls_md5_context;
+#else /* MBEDTLS_MD5_ALT */
+#include "md5_alt.h"
+#endif /* MBEDTLS_MD5_ALT */
+
/**
* \brief Initialize MD5 context
*
@@ -238,18 +242,6 @@ MBEDTLS_DEPRECATED void mbedtls_md5_process( mbedtls_md5_context *ctx,
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_MD5_ALT */
-#include "md5_alt.h"
-#endif /* MBEDTLS_MD5_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Output = MD5( input buffer )
*
diff --git a/thirdparty/mbedtls/include/mbedtls/net_sockets.h b/thirdparty/mbedtls/include/mbedtls/net_sockets.h
index 54e612cc5e..0f9b31ebcb 100644
--- a/thirdparty/mbedtls/include/mbedtls/net_sockets.h
+++ b/thirdparty/mbedtls/include/mbedtls/net_sockets.h
@@ -46,12 +46,17 @@
#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /**< Failed to get an IP address for the given hostname. */
#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /**< Buffer is too small to hold the data. */
#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /**< The context is invalid, eg because it was free()ed. */
+#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 /**< Polling the net context failed. */
+#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 /**< Input invalid. */
#define MBEDTLS_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */
#define MBEDTLS_NET_PROTO_TCP 0 /**< The TCP transport protocol */
#define MBEDTLS_NET_PROTO_UDP 1 /**< The UDP transport protocol */
+#define MBEDTLS_NET_POLL_READ 1 /**< Used in \c mbedtls_net_poll to check for pending data */
+#define MBEDTLS_NET_POLL_WRITE 2 /**< Used in \c mbedtls_net_poll to check if write possible */
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -118,9 +123,10 @@ int mbedtls_net_bind( mbedtls_net_context *ctx, const char *bind_ip, const char
*
* \param bind_ctx Relevant socket
* \param client_ctx Will contain the connected client socket
- * \param client_ip Will contain the client IP address
+ * \param client_ip Will contain the client IP address, can be NULL
* \param buf_size Size of the client_ip buffer
- * \param ip_len Will receive the size of the client IP written
+ * \param ip_len Will receive the size of the client IP written,
+ * can be NULL if client_ip is null
*
* \return 0 if successful, or
* MBEDTLS_ERR_NET_ACCEPT_FAILED, or
@@ -133,6 +139,29 @@ int mbedtls_net_accept( mbedtls_net_context *bind_ctx,
void *client_ip, size_t buf_size, size_t *ip_len );
/**
+ * \brief Check and wait for the context to be ready for read/write
+ *
+ * \param ctx Socket to check
+ * \param rw Bitflag composed of MBEDTLS_NET_POLL_READ and
+ * MBEDTLS_NET_POLL_WRITE specifying the events
+ * to wait for:
+ * - If MBEDTLS_NET_POLL_READ is set, the function
+ * will return as soon as the net context is available
+ * for reading.
+ * - If MBEDTLS_NET_POLL_WRITE is set, the function
+ * will return as soon as the net context is available
+ * for writing.
+ * \param timeout Maximal amount of time to wait before returning,
+ * in milliseconds. If \c timeout is zero, the
+ * function returns immediately. If \c timeout is
+ * -1u, the function blocks potentially indefinitely.
+ *
+ * \return Bitmask composed of MBEDTLS_NET_POLL_READ/WRITE
+ * on success or timeout, or a negative return code otherwise.
+ */
+int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout );
+
+/**
* \brief Set the socket blocking
*
* \param ctx Socket to set
diff --git a/thirdparty/mbedtls/include/mbedtls/pk.h b/thirdparty/mbedtls/include/mbedtls/pk.h
index 1059bdaa5b..ee06b2fd20 100644
--- a/thirdparty/mbedtls/include/mbedtls/pk.h
+++ b/thirdparty/mbedtls/include/mbedtls/pk.h
@@ -63,7 +63,7 @@
#define MBEDTLS_ERR_PK_INVALID_ALG -0x3A80 /**< The algorithm tag or value is invalid. */
#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00 /**< Elliptic curve is unsupported (only NIST curves are supported). */
#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980 /**< Unavailable feature, e.g. RSA disabled for RSA key. */
-#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH -0x3900 /**< The signature is valid but its length is less than expected. */
+#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH -0x3900 /**< The buffer contains a valid signature followed by more data. */
#define MBEDTLS_ERR_PK_HW_ACCEL_FAILED -0x3880 /**< PK hardware accelerator failed. */
#ifdef __cplusplus
@@ -269,8 +269,8 @@ int mbedtls_pk_can_do( const mbedtls_pk_context *ctx, mbedtls_pk_type_t type );
* \param sig_len Signature length
*
* \return 0 on success (signature is valid),
- * MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if the signature is
- * valid but its actual length is less than sig_len,
+ * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid
+ * signature in sig but its length is less than \p siglen,
* or a specific error code.
*
* \note For RSA keys, the default padding type is PKCS#1 v1.5.
@@ -300,10 +300,10 @@ int mbedtls_pk_verify( mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg,
* \param sig_len Signature length
*
* \return 0 on success (signature is valid),
- * MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be
+ * #MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be
* used for this type of signatures,
- * MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if the signature is
- * valid but its actual length is less than sig_len,
+ * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid
+ * signature in sig but its length is less than \p siglen,
* or a specific error code.
*
* \note If hash_len is 0, then the length associated with md_alg
diff --git a/thirdparty/mbedtls/include/mbedtls/platform.h b/thirdparty/mbedtls/include/mbedtls/platform.h
index ed10775848..bba770911e 100644
--- a/thirdparty/mbedtls/include/mbedtls/platform.h
+++ b/thirdparty/mbedtls/include/mbedtls/platform.h
@@ -1,7 +1,16 @@
/**
* \file platform.h
*
- * \brief The Mbed TLS platform abstraction layer.
+ * \brief This file contains the definitions and functions of the
+ * Mbed TLS platform abstraction layer.
+ *
+ * The platform abstraction layer removes the need for the library
+ * to directly link to standard C library functions or operating
+ * system services, making the library easier to port and embed.
+ * Application developers and users of the library can provide their own
+ * implementations of these functions, or implementations specific to
+ * their platform, which can be statically linked to the library or
+ * dynamically configured at runtime.
*/
/*
* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved
@@ -102,7 +111,7 @@ extern "C" {
/* \} name SECTION: Module settings */
/*
- * The function pointers for calloc and free
+ * The function pointers for calloc and free.
*/
#if defined(MBEDTLS_PLATFORM_MEMORY)
#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && \
@@ -116,7 +125,8 @@ extern void * (*mbedtls_calloc)( size_t n, size_t size );
extern void (*mbedtls_free)( void *ptr );
/**
- * \brief This function allows configuring custom memory-management functions.
+ * \brief This function dynamically sets the memory-management
+ * functions used by the library, during runtime.
*
* \param calloc_func The \c calloc function implementation.
* \param free_func The \c free function implementation.
@@ -140,7 +150,9 @@ int mbedtls_platform_set_calloc_free( void * (*calloc_func)( size_t, size_t ),
extern int (*mbedtls_fprintf)( FILE *stream, const char *format, ... );
/**
- * \brief This function allows configuring a custom \p fprintf function pointer.
+ * \brief This function dynamically configures the fprintf
+ * function that is called when the
+ * mbedtls_fprintf() function is invoked by the library.
*
* \param fprintf_func The \c fprintf function implementation.
*
@@ -163,8 +175,9 @@ int mbedtls_platform_set_fprintf( int (*fprintf_func)( FILE *stream, const char
extern int (*mbedtls_printf)( const char *format, ... );
/**
- * \brief This function allows configuring a custom \c printf function
- * pointer.
+ * \brief This function dynamically configures the snprintf
+ * function that is called when the mbedtls_snprintf()
+ * function is invoked by the library.
*
* \param printf_func The \c printf function implementation.
*
@@ -197,12 +210,12 @@ int mbedtls_platform_win32_snprintf( char *s, size_t n, const char *fmt, ... );
extern int (*mbedtls_snprintf)( char * s, size_t n, const char * format, ... );
/**
- * \brief This function allows configuring a custom \c snprintf function
- * pointer.
+ * \brief This function allows configuring a custom
+ * \c snprintf function pointer.
*
* \param snprintf_func The \c snprintf function implementation.
*
- * \return \c 0 on success.
+ * \return \c 0 on success.
*/
int mbedtls_platform_set_snprintf( int (*snprintf_func)( char * s, size_t n,
const char * format, ... ) );
@@ -210,7 +223,7 @@ int mbedtls_platform_set_snprintf( int (*snprintf_func)( char * s, size_t n,
#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO)
#define mbedtls_snprintf MBEDTLS_PLATFORM_SNPRINTF_MACRO
#else
-#define mbedtls_snprintf snprintf
+#define mbedtls_snprintf MBEDTLS_PLATFORM_STD_SNPRINTF
#endif /* MBEDTLS_PLATFORM_SNPRINTF_MACRO */
#endif /* MBEDTLS_PLATFORM_SNPRINTF_ALT */
@@ -221,12 +234,13 @@ int mbedtls_platform_set_snprintf( int (*snprintf_func)( char * s, size_t n,
extern void (*mbedtls_exit)( int status );
/**
- * \brief This function allows configuring a custom \c exit function
- * pointer.
+ * \brief This function dynamically configures the exit
+ * function that is called when the mbedtls_exit()
+ * function is invoked by the library.
*
* \param exit_func The \c exit function implementation.
*
- * \return \c 0 on success.
+ * \return \c 0 on success.
*/
int mbedtls_platform_set_exit( void (*exit_func)( int status ) );
#else
@@ -302,7 +316,7 @@ int mbedtls_platform_set_nv_seed(
* setup or teardown operations.
*/
typedef struct {
- char dummy; /**< Placeholder member, as empty structs are not portable. */
+ char dummy; /**< A placeholder member, as empty structs are not portable. */
}
mbedtls_platform_context;
@@ -311,33 +325,34 @@ mbedtls_platform_context;
#endif /* !MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT */
/**
- * \brief This function performs any platform initialization operations.
- *
- * \param ctx The Mbed TLS context.
+ * \brief This function performs any platform-specific initialization
+ * operations.
*
- * \return \c 0 on success.
+ * \note This function should be called before any other library functions.
*
- * \note This function is intended to allow platform-specific initialization,
- * and should be called before any other library functions. Its
- * implementation is platform-specific, and unless
+ * Its implementation is platform-specific, and unless
* platform-specific code is provided, it does nothing.
*
- * Its use and whether it is necessary to call it is dependent on the
- * platform.
+ * \note The usage and necessity of this function is dependent on the platform.
+ *
+ * \param ctx The platform context.
+ *
+ * \return \c 0 on success.
*/
int mbedtls_platform_setup( mbedtls_platform_context *ctx );
/**
* \brief This function performs any platform teardown operations.
*
- * \param ctx The Mbed TLS context.
- *
* \note This function should be called after every other Mbed TLS module
* has been correctly freed using the appropriate free function.
+ *
* Its implementation is platform-specific, and unless
* platform-specific code is provided, it does nothing.
*
- * Its use and whether it is necessary to call it is dependent on the
- * platform.
+ * \note The usage and necessity of this function is dependent on the platform.
+ *
+ * \param ctx The platform context.
+ *
*/
void mbedtls_platform_teardown( mbedtls_platform_context *ctx );
diff --git a/thirdparty/mbedtls/include/mbedtls/platform_util.h b/thirdparty/mbedtls/include/mbedtls/platform_util.h
new file mode 100644
index 0000000000..84f0732eeb
--- /dev/null
+++ b/thirdparty/mbedtls/include/mbedtls/platform_util.h
@@ -0,0 +1,62 @@
+/**
+ * \file platform_util.h
+ *
+ * \brief Common and shared functions used by multiple modules in the Mbed TLS
+ * library.
+ */
+/*
+ * Copyright (C) 2018, Arm Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of Mbed TLS (https://tls.mbed.org)
+ */
+#ifndef MBEDTLS_PLATFORM_UTIL_H
+#define MBEDTLS_PLATFORM_UTIL_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Securely zeroize a buffer
+ *
+ * The function is meant to wipe the data contained in a buffer so
+ * that it can no longer be recovered even if the program memory
+ * is later compromised. Call this function on sensitive data
+ * stored on the stack before returning from a function, and on
+ * sensitive data stored on the heap before freeing the heap
+ * object.
+ *
+ * It is extremely difficult to guarantee that calls to
+ * mbedtls_platform_zeroize() are not removed by aggressive
+ * compiler optimizations in a portable way. For this reason, Mbed
+ * TLS provides the configuration option
+ * MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure
+ * mbedtls_platform_zeroize() to use a suitable implementation for
+ * their platform and needs
+ *
+ * \param buf Buffer to be zeroized
+ * \param len Length of the buffer in bytes
+ *
+ */
+void mbedtls_platform_zeroize( void *buf, size_t len );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MBEDTLS_PLATFORM_UTIL_H */
diff --git a/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
index 3a8b50a621..a0dac0c360 100644
--- a/thirdparty/mbedtls/include/mbedtls/ripemd160.h
+++ b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
@@ -35,14 +35,14 @@
#define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED -0x0031 /**< RIPEMD160 hardware accelerator failed */
-#if !defined(MBEDTLS_RIPEMD160_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_RIPEMD160_ALT)
+// Regular implementation
+//
+
/**
* \brief RIPEMD-160 context structure
*/
@@ -54,6 +54,10 @@ typedef struct
}
mbedtls_ripemd160_context;
+#else /* MBEDTLS_RIPEMD160_ALT */
+#include "ripemd160.h"
+#endif /* MBEDTLS_RIPEMD160_ALT */
+
/**
* \brief Initialize RIPEMD-160 context
*
@@ -178,18 +182,6 @@ MBEDTLS_DEPRECATED void mbedtls_ripemd160_process(
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_RIPEMD160_ALT */
-#include "ripemd160_alt.h"
-#endif /* MBEDTLS_RIPEMD160_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Output = RIPEMD-160( input buffer )
*
diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h
index 5548f3c127..19eb2ee74c 100644
--- a/thirdparty/mbedtls/include/mbedtls/rsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/rsa.h
@@ -1,11 +1,12 @@
/**
* \file rsa.h
*
- * \brief The RSA public-key cryptosystem.
+ * \brief This file provides an API for the RSA public-key cryptosystem.
*
- * For more information, see <em>Public-Key Cryptography Standards (PKCS)
- * #1 v1.5: RSA Encryption</em> and <em>Public-Key Cryptography Standards
- * (PKCS) #1 v2.1: RSA Cryptography Specifications</em>.
+ * The RSA public-key cryptosystem is defined in <em>Public-Key
+ * Cryptography Standards (PKCS) #1 v1.5: RSA Encryption</em>
+ * and <em>Public-Key Cryptography Standards (PKCS) #1 v2.1:
+ * RSA Cryptography Specifications</em>.
*
*/
/*
@@ -63,8 +64,8 @@
#define MBEDTLS_RSA_PUBLIC 0 /**< Request private key operation. */
#define MBEDTLS_RSA_PRIVATE 1 /**< Request public key operation. */
-#define MBEDTLS_RSA_PKCS_V15 0 /**< Use PKCS-1 v1.5 encoding. */
-#define MBEDTLS_RSA_PKCS_V21 1 /**< Use PKCS-1 v2.1 encoding. */
+#define MBEDTLS_RSA_PKCS_V15 0 /**< Use PKCS#1 v1.5 encoding. */
+#define MBEDTLS_RSA_PKCS_V21 1 /**< Use PKCS#1 v2.1 encoding. */
#define MBEDTLS_RSA_SIGN 1 /**< Identifier for RSA signature operations. */
#define MBEDTLS_RSA_CRYPT 2 /**< Identifier for RSA encryption and decryption operations. */
@@ -76,14 +77,14 @@
* eg for alternative (PKCS#11) RSA implemenations in the PK layers.
*/
-#if !defined(MBEDTLS_RSA_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_RSA_ALT)
+// Regular implementation
+//
+
/**
* \brief The RSA context structure.
*
@@ -96,24 +97,24 @@ typedef struct
int ver; /*!< Always 0.*/
size_t len; /*!< The size of \p N in Bytes. */
- mbedtls_mpi N; /*!< The public modulus. */
- mbedtls_mpi E; /*!< The public exponent. */
+ mbedtls_mpi N; /*!< The public modulus. */
+ mbedtls_mpi E; /*!< The public exponent. */
- mbedtls_mpi D; /*!< The private exponent. */
- mbedtls_mpi P; /*!< The first prime factor. */
- mbedtls_mpi Q; /*!< The second prime factor. */
+ mbedtls_mpi D; /*!< The private exponent. */
+ mbedtls_mpi P; /*!< The first prime factor. */
+ mbedtls_mpi Q; /*!< The second prime factor. */
- mbedtls_mpi DP; /*!< \p D % (P - 1) */
- mbedtls_mpi DQ; /*!< \p D % (Q - 1) */
- mbedtls_mpi QP; /*!< 1 / (Q % P) */
+ mbedtls_mpi DP; /*!< <code>D % (P - 1)</code>. */
+ mbedtls_mpi DQ; /*!< <code>D % (Q - 1)</code>. */
+ mbedtls_mpi QP; /*!< <code>1 / (Q % P)</code>. */
- mbedtls_mpi RN; /*!< cached R^2 mod \p N */
+ mbedtls_mpi RN; /*!< cached <code>R^2 mod N</code>. */
- mbedtls_mpi RP; /*!< cached R^2 mod \p P */
- mbedtls_mpi RQ; /*!< cached R^2 mod \p Q */
+ mbedtls_mpi RP; /*!< cached <code>R^2 mod P</code>. */
+ mbedtls_mpi RQ; /*!< cached <code>R^2 mod Q</code>. */
- mbedtls_mpi Vi; /*!< The cached blinding value. */
- mbedtls_mpi Vf; /*!< The cached un-blinding value. */
+ mbedtls_mpi Vi; /*!< The cached blinding value. */
+ mbedtls_mpi Vf; /*!< The cached un-blinding value. */
int padding; /*!< Selects padding mode:
#MBEDTLS_RSA_PKCS_V15 for 1.5 padding and
@@ -128,18 +129,16 @@ typedef struct
}
mbedtls_rsa_context;
+#else /* MBEDTLS_RSA_ALT */
+#include "rsa_alt.h"
+#endif /* MBEDTLS_RSA_ALT */
+
/**
* \brief This function initializes an RSA context.
*
* \note Set padding to #MBEDTLS_RSA_PKCS_V21 for the RSAES-OAEP
* encryption scheme and the RSASSA-PSS signature scheme.
*
- * \param ctx The RSA context to initialize.
- * \param padding Selects padding mode: #MBEDTLS_RSA_PKCS_V15 or
- * #MBEDTLS_RSA_PKCS_V21.
- * \param hash_id The hash identifier of #mbedtls_md_type_t type, if
- * \p padding is #MBEDTLS_RSA_PKCS_V21.
- *
* \note The \p hash_id parameter is ignored when using
* #MBEDTLS_RSA_PKCS_V15 padding.
*
@@ -153,6 +152,12 @@ mbedtls_rsa_context;
* encryption. For PSS signatures, it is always used for
* making signatures, but can be overriden for verifying them.
* If set to #MBEDTLS_MD_NONE, it is always overriden.
+ *
+ * \param ctx The RSA context to initialize.
+ * \param padding Selects padding mode: #MBEDTLS_RSA_PKCS_V15 or
+ * #MBEDTLS_RSA_PKCS_V21.
+ * \param hash_id The hash identifier of #mbedtls_md_type_t type, if
+ * \p padding is #MBEDTLS_RSA_PKCS_V21.
*/
void mbedtls_rsa_init( mbedtls_rsa_context *ctx,
int padding,
@@ -162,13 +167,6 @@ void mbedtls_rsa_init( mbedtls_rsa_context *ctx,
* \brief This function imports a set of core parameters into an
* RSA context.
*
- * \param ctx The initialized RSA context to store the parameters in.
- * \param N The RSA modulus, or NULL.
- * \param P The first prime factor of \p N, or NULL.
- * \param Q The second prime factor of \p N, or NULL.
- * \param D The private exponent, or NULL.
- * \param E The public exponent, or NULL.
- *
* \note This function can be called multiple times for successive
* imports, if the parameters are not simultaneously present.
*
@@ -184,7 +182,15 @@ void mbedtls_rsa_init( mbedtls_rsa_context *ctx,
* \note The imported parameters are copied and need not be preserved
* for the lifetime of the RSA context being set up.
*
- * \return \c 0 on success, or a non-zero error code on failure.
+ * \param ctx The initialized RSA context to store the parameters in.
+ * \param N The RSA modulus, or NULL.
+ * \param P The first prime factor of \p N, or NULL.
+ * \param Q The second prime factor of \p N, or NULL.
+ * \param D The private exponent, or NULL.
+ * \param E The public exponent, or NULL.
+ *
+ * \return \c 0 on success.
+ * \return A non-zero error code on failure.
*/
int mbedtls_rsa_import( mbedtls_rsa_context *ctx,
const mbedtls_mpi *N,
@@ -195,18 +201,6 @@ int mbedtls_rsa_import( mbedtls_rsa_context *ctx,
* \brief This function imports core RSA parameters, in raw big-endian
* binary format, into an RSA context.
*
- * \param ctx The initialized RSA context to store the parameters in.
- * \param N The RSA modulus, or NULL.
- * \param N_len The Byte length of \p N, ignored if \p N == NULL.
- * \param P The first prime factor of \p N, or NULL.
- * \param P_len The Byte length of \p P, ignored if \p P == NULL.
- * \param Q The second prime factor of \p N, or NULL.
- * \param Q_len The Byte length of \p Q, ignored if \p Q == NULL.
- * \param D The private exponent, or NULL.
- * \param D_len The Byte length of \p D, ignored if \p D == NULL.
- * \param E The public exponent, or NULL.
- * \param E_len The Byte length of \p E, ignored if \p E == NULL.
- *
* \note This function can be called multiple times for successive
* imports, if the parameters are not simultaneously present.
*
@@ -222,7 +216,20 @@ int mbedtls_rsa_import( mbedtls_rsa_context *ctx,
* \note The imported parameters are copied and need not be preserved
* for the lifetime of the RSA context being set up.
*
- * \return \c 0 on success, or a non-zero error code on failure.
+ * \param ctx The initialized RSA context to store the parameters in.
+ * \param N The RSA modulus, or NULL.
+ * \param N_len The Byte length of \p N, ignored if \p N == NULL.
+ * \param P The first prime factor of \p N, or NULL.
+ * \param P_len The Byte length of \p P, ignored if \p P == NULL.
+ * \param Q The second prime factor of \p N, or NULL.
+ * \param Q_len The Byte length of \p Q, ignored if \p Q == NULL.
+ * \param D The private exponent, or NULL.
+ * \param D_len The Byte length of \p D, ignored if \p D == NULL.
+ * \param E The public exponent, or NULL.
+ * \param E_len The Byte length of \p E, ignored if \p E == NULL.
+ *
+ * \return \c 0 on success.
+ * \return A non-zero error code on failure.
*/
int mbedtls_rsa_import_raw( mbedtls_rsa_context *ctx,
unsigned char const *N, size_t N_len,
@@ -250,17 +257,18 @@ int mbedtls_rsa_import_raw( mbedtls_rsa_context *ctx,
* the RSA context can be used for RSA operations without
* the risk of failure or crash.
*
- * \param ctx The initialized RSA context holding imported parameters.
- *
- * \return \c 0 on success, or #MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the
- * attempted derivations failed.
- *
* \warning This function need not perform consistency checks
* for the imported parameters. In particular, parameters that
* are not needed by the implementation might be silently
* discarded and left unchecked. To check the consistency
* of the key material, see mbedtls_rsa_check_privkey().
*
+ * \param ctx The initialized RSA context holding imported parameters.
+ *
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the attempted derivations
+ * failed.
+ *
*/
int mbedtls_rsa_complete( mbedtls_rsa_context *ctx );
@@ -292,11 +300,11 @@ int mbedtls_rsa_complete( mbedtls_rsa_context *ctx );
* \param D The MPI to hold the private exponent, or NULL.
* \param E The MPI to hold the public exponent, or NULL.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION if exporting the
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION if exporting the
* requested parameters cannot be done due to missing
- * functionality or because of security policies,
- * or a non-zero return code on any other failure.
+ * functionality or because of security policies.
+ * \return A non-zero return code on any other failure.
*
*/
int mbedtls_rsa_export( const mbedtls_rsa_context *ctx,
@@ -324,6 +332,9 @@ int mbedtls_rsa_export( const mbedtls_rsa_context *ctx,
* If the function fails due to an unsupported operation,
* the RSA context stays intact and remains usable.
*
+ * \note The length parameters are ignored if the corresponding
+ * buffer pointers are NULL.
+ *
* \param ctx The initialized RSA context.
* \param N The Byte array to store the RSA modulus, or NULL.
* \param N_len The size of the buffer for the modulus.
@@ -331,21 +342,18 @@ int mbedtls_rsa_export( const mbedtls_rsa_context *ctx,
* NULL.
* \param P_len The size of the buffer for the first prime factor.
* \param Q The Byte array to hold the second prime factor of \p N, or
- NULL.
+ * NULL.
* \param Q_len The size of the buffer for the second prime factor.
* \param D The Byte array to hold the private exponent, or NULL.
* \param D_len The size of the buffer for the private exponent.
* \param E The Byte array to hold the public exponent, or NULL.
* \param E_len The size of the buffer for the public exponent.
*
- * \note The length fields are ignored if the corresponding
- * buffer pointers are NULL.
- *
- * \return \c 0 on success,
- * #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION if exporting the
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION if exporting the
* requested parameters cannot be done due to missing
- * functionality or because of security policies,
- * or a non-zero return code on any other failure.
+ * functionality or because of security policies.
+ * \return A non-zero return code on any other failure.
*/
int mbedtls_rsa_export_raw( const mbedtls_rsa_context *ctx,
unsigned char *N, size_t N_len,
@@ -357,16 +365,17 @@ int mbedtls_rsa_export_raw( const mbedtls_rsa_context *ctx,
/**
* \brief This function exports CRT parameters of a private RSA key.
*
+ * \note Alternative RSA implementations not using CRT-parameters
+ * internally can implement this function based on
+ * mbedtls_rsa_deduce_opt().
+ *
* \param ctx The initialized RSA context.
* \param DP The MPI to hold D modulo P-1, or NULL.
* \param DQ The MPI to hold D modulo Q-1, or NULL.
* \param QP The MPI to hold modular inverse of Q modulo P, or NULL.
*
- * \return \c 0 on success, non-zero error code otherwise.
- *
- * \note Alternative RSA implementations not using CRT-parameters
- * internally can implement this function based on
- * mbedtls_rsa_deduce_opt().
+ * \return \c 0 on success.
+ * \return A non-zero error code on failure.
*
*/
int mbedtls_rsa_export_crt( const mbedtls_rsa_context *ctx,
@@ -397,17 +406,17 @@ size_t mbedtls_rsa_get_len( const mbedtls_rsa_context *ctx );
/**
* \brief This function generates an RSA keypair.
*
+ * \note mbedtls_rsa_init() must be called before this function,
+ * to set up the RSA context.
+ *
* \param ctx The RSA context used to hold the key.
* \param f_rng The RNG function.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
* \param nbits The size of the public key in bits.
* \param exponent The public exponent. For example, 65537.
*
- * \note mbedtls_rsa_init() must be called before this function,
- * to set up the RSA context.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -424,8 +433,8 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
*
* \param ctx The RSA context to check.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*
*/
int mbedtls_rsa_check_pubkey( const mbedtls_rsa_context *ctx );
@@ -434,11 +443,6 @@ int mbedtls_rsa_check_pubkey( const mbedtls_rsa_context *ctx );
* \brief This function checks if a context contains an RSA private key
* and perform basic consistency checks.
*
- * \param ctx The RSA context to check.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code on
- * failure.
- *
* \note The consistency checks performed by this function not only
* ensure that mbedtls_rsa_private() can be called successfully
* on the given context, but that the various parameters are
@@ -465,6 +469,11 @@ int mbedtls_rsa_check_pubkey( const mbedtls_rsa_context *ctx );
* user to ensure the trustworthiness of the source of his RSA
* parameters, which goes beyond what is effectively checkable
* by the library.</li></ul>
+ *
+ * \param ctx The RSA context to check.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_check_privkey( const mbedtls_rsa_context *ctx );
@@ -476,8 +485,8 @@ int mbedtls_rsa_check_privkey( const mbedtls_rsa_context *ctx );
* \param pub The RSA context holding the public key.
* \param prv The RSA context holding the private key.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_check_pub_priv( const mbedtls_rsa_context *pub,
const mbedtls_rsa_context *prv );
@@ -485,13 +494,6 @@ int mbedtls_rsa_check_pub_priv( const mbedtls_rsa_context *pub,
/**
* \brief This function performs an RSA public key operation.
*
- * \param ctx The RSA context.
- * \param input The input buffer.
- * \param output The output buffer.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
* \note This function does not handle message padding.
*
* \note Make sure to set \p input[0] = 0 or ensure that
@@ -499,6 +501,13 @@ int mbedtls_rsa_check_pub_priv( const mbedtls_rsa_context *pub,
*
* \note The input and output buffers must be large
* enough. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \param ctx The RSA context.
+ * \param input The input buffer.
+ * \param output The output buffer.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_public( mbedtls_rsa_context *ctx,
const unsigned char *input,
@@ -507,15 +516,6 @@ int mbedtls_rsa_public( mbedtls_rsa_context *ctx,
/**
* \brief This function performs an RSA private key operation.
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Needed for blinding.
- * \param p_rng The RNG parameter.
- * \param input The input buffer.
- * \param output The output buffer.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
* \note The input and output buffers must be large
* enough. For example, 128 Bytes if RSA-1024 is used.
*
@@ -530,6 +530,15 @@ int mbedtls_rsa_public( mbedtls_rsa_context *ctx,
* Future versions of the library may enforce the presence
* of a PRNG.
*
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Needed for blinding.
+ * \param p_rng The RNG context.
+ * \param input The input buffer.
+ * \param output The output buffer.
+ *
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
+ *
*/
int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -544,15 +553,8 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
* It is the generic wrapper for performing a PKCS#1 encryption
* operation using the \p mode from the context.
*
- *
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Needed for padding, PKCS#1 v2.1
- * encoding, and #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param ilen The length of the plaintext.
- * \param input The buffer holding the data to encrypt.
- * \param output The buffer used to hold the ciphertext.
+ * \note The input and output buffers must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -563,11 +565,17 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PRIVATE and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Needed for padding, PKCS#1 v2.1
+ * encoding, and #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param ilen The length of the plaintext.
+ * \param input The buffer holding the data to encrypt.
+ * \param output The buffer used to hold the ciphertext.
*
- * \note The input and output buffers must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_pkcs1_encrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -580,14 +588,8 @@ int mbedtls_rsa_pkcs1_encrypt( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v1.5 encryption operation
* (RSAES-PKCS1-v1_5-ENCRYPT).
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Needed for padding and
- * #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param ilen The length of the plaintext.
- * \param input The buffer holding the data to encrypt.
- * \param output The buffer used to hold the ciphertext.
+ * \note The output buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -598,11 +600,17 @@ int mbedtls_rsa_pkcs1_encrypt( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PRIVATE and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Needed for padding and
+ * #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param ilen The length of the plaintext.
+ * \param input The buffer holding the data to encrypt.
+ * \param output The buffer used to hold the ciphertext.
*
- * \note The output buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsaes_pkcs1_v15_encrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -615,10 +623,22 @@ int mbedtls_rsa_rsaes_pkcs1_v15_encrypt( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v2.1 OAEP encryption
* operation (RSAES-OAEP-ENCRYPT).
*
+ * \note The output buffer must be as large as the size
+ * of ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \deprecated It is deprecated and discouraged to call this function
+ * in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
+ * are likely to remove the \p mode argument and have it
+ * implicitly set to #MBEDTLS_RSA_PUBLIC.
+ *
+ * \note Alternative implementations of RSA need not support
+ * mode being set to #MBEDTLS_RSA_PRIVATE and might instead
+ * return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
+ *
* \param ctx The RSA context.
* \param f_rng The RNG function. Needed for padding and PKCS#1 v2.1
* encoding and #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
* \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
* \param label The buffer holding the custom label to use.
* \param label_len The length of the label.
@@ -626,20 +646,8 @@ int mbedtls_rsa_rsaes_pkcs1_v15_encrypt( mbedtls_rsa_context *ctx,
* \param input The buffer holding the data to encrypt.
* \param output The buffer used to hold the ciphertext.
*
- * \deprecated It is deprecated and discouraged to call this function
- * in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
- * are likely to remove the \p mode argument and have it
- * implicitly set to #MBEDTLS_RSA_PUBLIC.
- *
- * \note Alternative implementations of RSA need not support
- * mode being set to #MBEDTLS_RSA_PRIVATE and might instead
- * return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The output buffer must be as large as the size
- * of ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsaes_oaep_encrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -657,14 +665,15 @@ int mbedtls_rsa_rsaes_oaep_encrypt( mbedtls_rsa_context *ctx,
* It is the generic wrapper for performing a PKCS#1 decryption
* operation using the \p mode from the context.
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param olen The length of the plaintext.
- * \param input The buffer holding the encrypted data.
- * \param output The buffer used to hold the plaintext.
- * \param output_max_len The maximum length of the output buffer.
+ * \note The output buffer length \c output_max_len should be
+ * as large as the size \p ctx->len of \p ctx->N (for example,
+ * 128 Bytes if RSA-1024 is used) to be able to hold an
+ * arbitrary decrypted message. If it is not large enough to
+ * hold the decryption of the particular ciphertext provided,
+ * the function returns \c MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
+ *
+ * \note The input buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
@@ -675,18 +684,17 @@ int mbedtls_rsa_rsaes_oaep_encrypt( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PUBLIC and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The output buffer length \c output_max_len should be
- * as large as the size \p ctx->len of \p ctx->N (for example,
- * 128 Bytes if RSA-1024 is used) to be able to hold an
- * arbitrary decrypted message. If it is not large enough to
- * hold the decryption of the particular ciphertext provided,
- * the function returns \c MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param olen The length of the plaintext.
+ * \param input The buffer holding the encrypted data.
+ * \param output The buffer used to hold the plaintext.
+ * \param output_max_len The maximum length of the output buffer.
*
- * \note The input buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_pkcs1_decrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -700,14 +708,15 @@ int mbedtls_rsa_pkcs1_decrypt( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v1.5 decryption
* operation (RSAES-PKCS1-v1_5-DECRYPT).
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param olen The length of the plaintext.
- * \param input The buffer holding the encrypted data.
- * \param output The buffer to hold the plaintext.
- * \param output_max_len The maximum length of the output buffer.
+ * \note The output buffer length \c output_max_len should be
+ * as large as the size \p ctx->len of \p ctx->N, for example,
+ * 128 Bytes if RSA-1024 is used, to be able to hold an
+ * arbitrary decrypted message. If it is not large enough to
+ * hold the decryption of the particular ciphertext provided,
+ * the function returns #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
+ *
+ * \note The input buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
@@ -718,18 +727,18 @@ int mbedtls_rsa_pkcs1_decrypt( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PUBLIC and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param olen The length of the plaintext.
+ * \param input The buffer holding the encrypted data.
+ * \param output The buffer to hold the plaintext.
+ * \param output_max_len The maximum length of the output buffer.
*
- * \note The output buffer length \c output_max_len should be
- * as large as the size \p ctx->len of \p ctx->N, for example,
- * 128 Bytes if RSA-1024 is used, to be able to hold an
- * arbitrary decrypted message. If it is not large enough to
- * hold the decryption of the particular ciphertext provided,
- * the function returns #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*
- * \note The input buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*/
int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -740,12 +749,32 @@ int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
size_t output_max_len );
/**
- * \brief This function performs a PKCS#1 v2.1 OAEP decryption
- * operation (RSAES-OAEP-DECRYPT).
+ * \brief This function performs a PKCS#1 v2.1 OAEP decryption
+ * operation (RSAES-OAEP-DECRYPT).
+ *
+ * \note The output buffer length \c output_max_len should be
+ * as large as the size \p ctx->len of \p ctx->N, for
+ * example, 128 Bytes if RSA-1024 is used, to be able to
+ * hold an arbitrary decrypted message. If it is not
+ * large enough to hold the decryption of the particular
+ * ciphertext provided, the function returns
+ * #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
+ *
+ * \note The input buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \deprecated It is deprecated and discouraged to call this function
+ * in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
+ * are likely to remove the \p mode argument and have it
+ * implicitly set to #MBEDTLS_RSA_PRIVATE.
+ *
+ * \note Alternative implementations of RSA need not support
+ * mode being set to #MBEDTLS_RSA_PUBLIC and might instead
+ * return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
* \param ctx The RSA context.
* \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
* \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
* \param label The buffer holding the custom label to use.
* \param label_len The length of the label.
@@ -754,28 +783,8 @@ int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
* \param output The buffer to hold the plaintext.
* \param output_max_len The maximum length of the output buffer.
*
- * \deprecated It is deprecated and discouraged to call this function
- * in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
- * are likely to remove the \p mode argument and have it
- * implicitly set to #MBEDTLS_RSA_PRIVATE.
- *
- * \note Alternative implementations of RSA need not support
- * mode being set to #MBEDTLS_RSA_PUBLIC and might instead
- * return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
- *
- * \return \c 0 on success, or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The output buffer length \c output_max_len should be
- * as large as the size \p ctx->len of \p ctx->N, for
- * example, 128 Bytes if RSA-1024 is used, to be able to
- * hold an arbitrary decrypted message. If it is not
- * large enough to hold the decryption of the particular
- * ciphertext provided, the function returns
- * #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE.
- *
- * \note The input buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 on success.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsaes_oaep_decrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -794,16 +803,12 @@ int mbedtls_rsa_rsaes_oaep_decrypt( mbedtls_rsa_context *ctx,
* It is the generic wrapper for performing a PKCS#1
* signature using the \p mode from the context.
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Needed for PKCS#1 v2.1 encoding and for
- * #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer to hold the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note For PKCS#1 v2.1 encoding, see comments on
+ * mbedtls_rsa_rsassa_pss_sign() for details on
+ * \p md_alg and \p hash_id.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
@@ -814,15 +819,19 @@ int mbedtls_rsa_rsaes_oaep_decrypt( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PUBLIC and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the signing operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code on failure.
- *
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Needed for PKCS#1 v2.1 encoding and for
+ * #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer to hold the ciphertext.
*
- * \note For PKCS#1 v2.1 encoding, see comments on
- * mbedtls_rsa_rsassa_pss_sign() for details on
- * \p md_alg and \p hash_id.
+ * \return \c 0 if the signing operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -837,15 +846,8 @@ int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v1.5 signature
* operation (RSASSA-PKCS1-v1_5-SIGN).
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer to hold the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
@@ -856,12 +858,18 @@ int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PUBLIC and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the signing operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer to hold the ciphertext.
*
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 if the signing operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsassa_pkcs1_v15_sign( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -876,16 +884,15 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v2.1 PSS signature
* operation (RSASSA-PSS-SIGN).
*
- * \param ctx The RSA context.
- * \param f_rng The RNG function. Needed for PKCS#1 v2.1 encoding and for
- * #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer to hold the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note The \p hash_id in the RSA context is the one used for the
+ * encoding. \p md_alg in the function call is the type of hash
+ * that is encoded. According to <em>RFC-3447: Public-Key
+ * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
+ * Specifications</em> it is advised to keep both hashes the
+ * same.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
@@ -896,19 +903,19 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PUBLIC and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the signing operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \param ctx The RSA context.
+ * \param f_rng The RNG function. Needed for PKCS#1 v2.1 encoding and for
+ * #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer to hold the ciphertext.
*
- * \note The \p hash_id in the RSA context is the one used for the
- * encoding. \p md_alg in the function call is the type of hash
- * that is encoded. According to <em>RFC-3447: Public-Key
- * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
- * Specifications</em> it is advised to keep both hashes the
- * same.
+ * \return \c 0 if the signing operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsassa_pss_sign( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -926,15 +933,12 @@ int mbedtls_rsa_rsassa_pss_sign( mbedtls_rsa_context *ctx,
* This is the generic wrapper for performing a PKCS#1
* verification using the mode from the context.
*
- * \param ctx The RSA public key context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer holding the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note For PKCS#1 v2.1 encoding, see comments on
+ * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and
+ * \p hash_id.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -945,16 +949,18 @@ int mbedtls_rsa_rsassa_pss_sign( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PRIVATE and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the verify operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \param ctx The RSA public key context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer holding the ciphertext.
*
- * \note For PKCS#1 v2.1 encoding, see comments on
- * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and
- * \p hash_id.
+ * \return \c 0 if the verify operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -969,15 +975,8 @@ int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx,
* \brief This function performs a PKCS#1 v1.5 verification
* operation (RSASSA-PKCS1-v1_5-VERIFY).
*
- * \param ctx The RSA public key context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer holding the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -988,12 +987,18 @@ int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PRIVATE and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the verify operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
+ * \param ctx The RSA public key context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer holding the ciphertext.
*
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \return \c 0 if the verify operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsassa_pkcs1_v15_verify( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -1011,15 +1016,16 @@ int mbedtls_rsa_rsassa_pkcs1_v15_verify( mbedtls_rsa_context *ctx,
* The hash function for the MGF mask generating function
* is that specified in the RSA context.
*
- * \param ctx The RSA public key context.
- * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
- * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
- * \param md_alg The message-digest algorithm used to hash the original data.
- * Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
- * \param hash The buffer holding the message digest.
- * \param sig The buffer holding the ciphertext.
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note The \p hash_id in the RSA context is the one used for the
+ * verification. \p md_alg in the function call is the type of
+ * hash that is verified. According to <em>RFC-3447: Public-Key
+ * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
+ * Specifications</em> it is advised to keep both hashes the
+ * same. If \p hash_id in the RSA context is unset,
+ * the \p md_alg from the function call is used.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -1030,20 +1036,18 @@ int mbedtls_rsa_rsassa_pkcs1_v15_verify( mbedtls_rsa_context *ctx,
* mode being set to #MBEDTLS_RSA_PRIVATE and might instead
* return #MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION.
*
- * \return \c 0 if the verify operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ * \param ctx The RSA public key context.
+ * \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
+ * \param p_rng The RNG context.
+ * \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
+ * \param md_alg The message-digest algorithm used to hash the original data.
+ * Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash The buffer holding the message digest.
+ * \param sig The buffer holding the ciphertext.
*
- * \note The \p hash_id in the RSA context is the one used for the
- * verification. \p md_alg in the function call is the type of
- * hash that is verified. According to <em>RFC-3447: Public-Key
- * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
- * Specifications</em> it is advised to keep both hashes the
- * same. If \p hash_id in the RSA context is unset,
- * the \p md_alg from the function call is used.
+ * \return \c 0 if the verify operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsassa_pss_verify( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -1061,27 +1065,27 @@ int mbedtls_rsa_rsassa_pss_verify( mbedtls_rsa_context *ctx,
* The hash function for the MGF mask generating function
* is that specified in \p mgf1_hash_id.
*
+ * \note The \p sig buffer must be as large as the size
+ * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note The \p hash_id in the RSA context is ignored.
+ *
* \param ctx The RSA public key context.
* \param f_rng The RNG function. Only needed for #MBEDTLS_RSA_PRIVATE.
- * \param p_rng The RNG parameter.
+ * \param p_rng The RNG context.
* \param mode #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE.
* \param md_alg The message-digest algorithm used to hash the original data.
* Use #MBEDTLS_MD_NONE for signing raw data.
- * \param hashlen The length of the message digest. Only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hashlen The length of the message digest. Only used if \p md_alg is
+ * #MBEDTLS_MD_NONE.
* \param hash The buffer holding the message digest.
- * \param mgf1_hash_id The message digest used for mask generation.
- * \param expected_salt_len The length of the salt used in padding. Use
- * #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length.
+ * \param mgf1_hash_id The message digest used for mask generation.
+ * \param expected_salt_len The length of the salt used in padding. Use
+ * #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length.
* \param sig The buffer holding the ciphertext.
*
- * \return \c 0 if the verify operation was successful,
- * or an \c MBEDTLS_ERR_RSA_XXX error code
- * on failure.
- *
- * \note The \p sig buffer must be as large as the size
- * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
- *
- * \note The \p hash_id in the RSA context is ignored.
+ * \return \c 0 if the verify operation was successful.
+ * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
*/
int mbedtls_rsa_rsassa_pss_verify_ext( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -1100,8 +1104,8 @@ int mbedtls_rsa_rsassa_pss_verify_ext( mbedtls_rsa_context *ctx,
* \param dst The destination context.
* \param src The source context.
*
- * \return \c 0 on success,
- * #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory allocation failure.
+ * \return \c 0 on success.
+ * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory allocation failure.
*/
int mbedtls_rsa_copy( mbedtls_rsa_context *dst, const mbedtls_rsa_context *src );
@@ -1112,22 +1116,11 @@ int mbedtls_rsa_copy( mbedtls_rsa_context *dst, const mbedtls_rsa_context *src )
*/
void mbedtls_rsa_free( mbedtls_rsa_context *ctx );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_RSA_ALT */
-#include "rsa_alt.h"
-#endif /* MBEDTLS_RSA_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief The RSA checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_rsa_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/rsa_internal.h b/thirdparty/mbedtls/include/mbedtls/rsa_internal.h
index bcb3c9401d..53abd3c5b0 100644
--- a/thirdparty/mbedtls/include/mbedtls/rsa_internal.h
+++ b/thirdparty/mbedtls/include/mbedtls/rsa_internal.h
@@ -2,6 +2,37 @@
* \file rsa_internal.h
*
* \brief Context-independent RSA helper functions
+ *
+ * This module declares some RSA-related helper functions useful when
+ * implementing the RSA interface. These functions are provided in a separate
+ * compilation unit in order to make it easy for designers of alternative RSA
+ * implementations to use them in their own code, as it is conceived that the
+ * functionality they provide will be necessary for most complete
+ * implementations.
+ *
+ * End-users of Mbed TLS who are not providing their own alternative RSA
+ * implementations should not use these functions directly, and should instead
+ * use only the functions declared in rsa.h.
+ *
+ * The interface provided by this module will be maintained through LTS (Long
+ * Term Support) branches of Mbed TLS, but may otherwise be subject to change,
+ * and must be considered an internal interface of the library.
+ *
+ * There are two classes of helper functions:
+ *
+ * (1) Parameter-generating helpers. These are:
+ * - mbedtls_rsa_deduce_primes
+ * - mbedtls_rsa_deduce_private_exponent
+ * - mbedtls_rsa_deduce_crt
+ * Each of these functions takes a set of core RSA parameters and
+ * generates some other, or CRT related parameters.
+ *
+ * (2) Parameter-checking helpers. These are:
+ * - mbedtls_rsa_validate_params
+ * - mbedtls_rsa_validate_crt
+ * They take a set of core or CRT related RSA parameters and check their
+ * validity.
+ *
*/
/*
* Copyright (C) 2006-2017, ARM Limited, All Rights Reserved
@@ -21,31 +52,6 @@
*
* This file is part of mbed TLS (https://tls.mbed.org)
*
- *
- * This file declares some RSA-related helper functions useful when
- * implementing the RSA interface. They are public and provided in a
- * separate compilation unit in order to make it easy for designers of
- * alternative RSA implementations to use them in their code, as it is
- * conceived that the functionality they provide will be necessary
- * for most complete implementations.
- *
- * End-users of Mbed TLS not intending to re-implement the RSA functionality
- * are not expected to get into the need of making use of these functions directly,
- * but instead should be able to use the functions declared in rsa.h.
- *
- * There are two classes of helper functions:
- * (1) Parameter-generating helpers. These are:
- * - mbedtls_rsa_deduce_primes
- * - mbedtls_rsa_deduce_private_exponent
- * - mbedtls_rsa_deduce_crt
- * Each of these functions takes a set of core RSA parameters
- * and generates some other, or CRT related parameters.
- * (2) Parameter-checking helpers. These are:
- * - mbedtls_rsa_validate_params
- * - mbedtls_rsa_validate_crt
- * They take a set of core or CRT related RSA parameters
- * and check their validity.
- *
*/
#ifndef MBEDTLS_RSA_INTERNAL_H
@@ -213,4 +219,8 @@ int mbedtls_rsa_validate_crt( const mbedtls_mpi *P, const mbedtls_mpi *Q,
const mbedtls_mpi *D, const mbedtls_mpi *DP,
const mbedtls_mpi *DQ, const mbedtls_mpi *QP );
+#ifdef __cplusplus
+}
+#endif
+
#endif /* rsa_internal.h */
diff --git a/thirdparty/mbedtls/include/mbedtls/sha1.h b/thirdparty/mbedtls/include/mbedtls/sha1.h
index 05540cde12..65a124c94b 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha1.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha1.h
@@ -1,7 +1,10 @@
/**
* \file sha1.h
*
- * \brief The SHA-1 cryptographic hash function.
+ * \brief This file contains SHA-1 definitions and functions.
+ *
+ * The Secure Hash Algorithm 1 (SHA-1) cryptographic hash function is defined in
+ * <em>FIPS 180-4: Secure Hash Standard (SHS)</em>.
*
* \warning SHA-1 is considered a weak message digest and its use constitutes
* a security risk. We recommend considering stronger message
@@ -39,14 +42,14 @@
#define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED -0x0035 /**< SHA-1 hardware accelerator failed */
-#if !defined(MBEDTLS_SHA1_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_SHA1_ALT)
+// Regular implementation
+//
+
/**
* \brief The SHA-1 context structure.
*
@@ -63,40 +66,44 @@ typedef struct
}
mbedtls_sha1_context;
+#else /* MBEDTLS_SHA1_ALT */
+#include "sha1_alt.h"
+#endif /* MBEDTLS_SHA1_ALT */
+
/**
* \brief This function initializes a SHA-1 context.
*
- * \param ctx The SHA-1 context to initialize.
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context to initialize.
+ *
*/
void mbedtls_sha1_init( mbedtls_sha1_context *ctx );
/**
* \brief This function clears a SHA-1 context.
*
- * \param ctx The SHA-1 context to clear.
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context to clear.
+ *
*/
void mbedtls_sha1_free( mbedtls_sha1_context *ctx );
/**
* \brief This function clones the state of a SHA-1 context.
*
- * \param dst The destination context.
- * \param src The context to clone.
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param dst The SHA-1 context to clone to.
+ * \param src The SHA-1 context to clone from.
+ *
*/
void mbedtls_sha1_clone( mbedtls_sha1_context *dst,
const mbedtls_sha1_context *src );
@@ -104,14 +111,14 @@ void mbedtls_sha1_clone( mbedtls_sha1_context *dst,
/**
* \brief This function starts a SHA-1 checksum calculation.
*
- * \param ctx The context to initialize.
- *
- * \return \c 0 if successful
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context to initialize.
+ *
+ * \return \c 0 on success.
+ *
*/
int mbedtls_sha1_starts_ret( mbedtls_sha1_context *ctx );
@@ -119,16 +126,15 @@ int mbedtls_sha1_starts_ret( mbedtls_sha1_context *ctx );
* \brief This function feeds an input buffer into an ongoing SHA-1
* checksum calculation.
*
- * \param ctx The SHA-1 context.
- * \param input The buffer holding the input data.
- * \param ilen The length of the input data.
- *
- * \return \c 0 if successful
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context.
+ * \param input The buffer holding the input data.
+ * \param ilen The length of the input data.
+ *
+ * \return \c 0 on success.
*/
int mbedtls_sha1_update_ret( mbedtls_sha1_context *ctx,
const unsigned char *input,
@@ -138,31 +144,30 @@ int mbedtls_sha1_update_ret( mbedtls_sha1_context *ctx,
* \brief This function finishes the SHA-1 operation, and writes
* the result to the output buffer.
*
- * \param ctx The SHA-1 context.
- * \param output The SHA-1 checksum result.
- *
- * \return \c 0 if successful
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context.
+ * \param output The SHA-1 checksum result.
+ *
+ * \return \c 0 on success.
*/
int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx,
unsigned char output[20] );
/**
- * \brief SHA-1 process data block (internal use only)
- *
- * \param ctx SHA-1 context
- * \param data The data block being processed.
- *
- * \return \c 0 if successful
+ * \brief SHA-1 process data block (internal use only).
*
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \param ctx The SHA-1 context.
+ * \param data The data block being processed.
+ *
+ * \return \c 0 on success.
+ *
*/
int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
const unsigned char data[64] );
@@ -174,65 +179,67 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
#define MBEDTLS_DEPRECATED
#endif
/**
- * \brief SHA-1 context setup
- *
- * \deprecated Superseded by mbedtls_sha1_starts_ret() in 2.7.0
- *
- * \param ctx The SHA-1 context to be initialized.
+ * \brief This function starts a SHA-1 checksum calculation.
*
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \deprecated Superseded by mbedtls_sha1_starts_ret() in 2.7.0.
+ *
+ * \param ctx The SHA-1 context to initialize.
+ *
*/
MBEDTLS_DEPRECATED void mbedtls_sha1_starts( mbedtls_sha1_context *ctx );
/**
- * \brief SHA-1 process buffer
+ * \brief This function feeds an input buffer into an ongoing SHA-1
+ * checksum calculation.
+ *
+ * \warning SHA-1 is considered a weak message digest and its use
+ * constitutes a security risk. We recommend considering
+ * stronger message digests instead.
*
- * \deprecated Superseded by mbedtls_sha1_update_ret() in 2.7.0
+ * \deprecated Superseded by mbedtls_sha1_update_ret() in 2.7.0.
*
* \param ctx The SHA-1 context.
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
*
- * \warning SHA-1 is considered a weak message digest and its use
- * constitutes a security risk. We recommend considering
- * stronger message digests instead.
- *
*/
MBEDTLS_DEPRECATED void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
const unsigned char *input,
size_t ilen );
/**
- * \brief SHA-1 final digest
- *
- * \deprecated Superseded by mbedtls_sha1_finish_ret() in 2.7.0
- *
- * \param ctx The SHA-1 context.
- * \param output The SHA-1 checksum result.
+ * \brief This function finishes the SHA-1 operation, and writes
+ * the result to the output buffer.
*
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \deprecated Superseded by mbedtls_sha1_finish_ret() in 2.7.0.
+ *
+ * \param ctx The SHA-1 context.
+ * \param output The SHA-1 checksum result.
+ *
*/
MBEDTLS_DEPRECATED void mbedtls_sha1_finish( mbedtls_sha1_context *ctx,
unsigned char output[20] );
/**
- * \brief SHA-1 process data block (internal use only)
- *
- * \deprecated Superseded by mbedtls_internal_sha1_process() in 2.7.0
- *
- * \param ctx The SHA-1 context.
- * \param data The data block being processed.
+ * \brief SHA-1 process data block (internal use only).
*
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \deprecated Superseded by mbedtls_internal_sha1_process() in 2.7.0.
+ *
+ * \param ctx The SHA-1 context.
+ * \param data The data block being processed.
+ *
*/
MBEDTLS_DEPRECATED void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
const unsigned char data[64] );
@@ -240,18 +247,6 @@ MBEDTLS_DEPRECATED void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_SHA1_ALT */
-#include "sha1_alt.h"
-#endif /* MBEDTLS_SHA1_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief This function calculates the SHA-1 checksum of a buffer.
*
@@ -261,15 +256,15 @@ extern "C" {
* The SHA-1 result is calculated as
* output = SHA-1(input buffer).
*
+ * \warning SHA-1 is considered a weak message digest and its use
+ * constitutes a security risk. We recommend considering
+ * stronger message digests instead.
+ *
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
* \param output The SHA-1 checksum result.
*
- * \return \c 0 if successful
- *
- * \warning SHA-1 is considered a weak message digest and its use
- * constitutes a security risk. We recommend considering
- * stronger message digests instead.
+ * \return \c 0 on success.
*
*/
int mbedtls_sha1_ret( const unsigned char *input,
@@ -283,18 +278,24 @@ int mbedtls_sha1_ret( const unsigned char *input,
#define MBEDTLS_DEPRECATED
#endif
/**
- * \brief Output = SHA-1( input buffer )
+ * \brief This function calculates the SHA-1 checksum of a buffer.
*
- * \deprecated Superseded by mbedtls_sha1_ret() in 2.7.0
+ * The function allocates the context, performs the
+ * calculation, and frees the context.
*
- * \param input The buffer holding the input data.
- * \param ilen The length of the input data.
- * \param output The SHA-1 checksum result.
+ * The SHA-1 result is calculated as
+ * output = SHA-1(input buffer).
*
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \deprecated Superseded by mbedtls_sha1_ret() in 2.7.0
+ *
+ * \param input The buffer holding the input data.
+ * \param ilen The length of the input data.
+ * \param output The SHA-1 checksum result.
+ *
*/
MBEDTLS_DEPRECATED void mbedtls_sha1( const unsigned char *input,
size_t ilen,
@@ -306,12 +307,13 @@ MBEDTLS_DEPRECATED void mbedtls_sha1( const unsigned char *input,
/**
* \brief The SHA-1 checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
- *
* \warning SHA-1 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
+ *
*/
int mbedtls_sha1_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/sha256.h b/thirdparty/mbedtls/include/mbedtls/sha256.h
index ffb16c277a..adf31a82ed 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha256.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha256.h
@@ -1,7 +1,10 @@
/**
* \file sha256.h
*
- * \brief The SHA-224 and SHA-256 cryptographic hash function.
+ * \brief This file contains SHA-224 and SHA-256 definitions and functions.
+ *
+ * The Secure Hash Algorithms 224 and 256 (SHA-224 and SHA-256) cryptographic
+ * hash functions are defined in <em>FIPS 180-4: Secure Hash Standard (SHS)</em>.
*/
/*
* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved
@@ -35,14 +38,14 @@
#define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED -0x0037 /**< SHA-256 hardware accelerator failed */
-#if !defined(MBEDTLS_SHA256_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_SHA256_ALT)
+// Regular implementation
+//
+
/**
* \brief The SHA-256 context structure.
*
@@ -55,12 +58,15 @@ typedef struct
uint32_t total[2]; /*!< The number of Bytes processed. */
uint32_t state[8]; /*!< The intermediate digest state. */
unsigned char buffer[64]; /*!< The data block being processed. */
- int is224; /*!< Determines which function to use.
- <ul><li>0: Use SHA-256.</li>
- <li>1: Use SHA-224.</li></ul> */
+ int is224; /*!< Determines which function to use:
+ 0: Use SHA-256, or 1: Use SHA-224. */
}
mbedtls_sha256_context;
+#else /* MBEDTLS_SHA256_ALT */
+#include "sha256_alt.h"
+#endif /* MBEDTLS_SHA256_ALT */
+
/**
* \brief This function initializes a SHA-256 context.
*
@@ -89,9 +95,8 @@ void mbedtls_sha256_clone( mbedtls_sha256_context *dst,
* calculation.
*
* \param ctx The context to initialize.
- * \param is224 Determines which function to use.
- * <ul><li>0: Use SHA-256.</li>
- * <li>1: Use SHA-224.</li></ul>
+ * \param is224 Determines which function to use:
+ * 0: Use SHA-256, or 1: Use SHA-224.
*
* \return \c 0 on success.
*/
@@ -101,9 +106,9 @@ int mbedtls_sha256_starts_ret( mbedtls_sha256_context *ctx, int is224 );
* \brief This function feeds an input buffer into an ongoing
* SHA-256 checksum calculation.
*
- * \param ctx SHA-256 context
- * \param input buffer holding the data
- * \param ilen length of the input data
+ * \param ctx The SHA-256 context.
+ * \param input The buffer holding the data.
+ * \param ilen The length of the input data.
*
* \return \c 0 on success.
*/
@@ -143,14 +148,15 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx,
#define MBEDTLS_DEPRECATED
#endif
/**
- * \brief This function starts a SHA-256 checksum calculation.
+ * \brief This function starts a SHA-224 or SHA-256 checksum
+ * calculation.
+ *
*
* \deprecated Superseded by mbedtls_sha256_starts_ret() in 2.7.0.
*
- * \param ctx The SHA-256 context to initialize.
- * \param is224 Determines which function to use.
- * <ul><li>0: Use SHA-256.</li>
- * <li>1: Use SHA-224.</li></ul>
+ * \param ctx The context to initialize.
+ * \param is224 Determines which function to use:
+ * 0: Use SHA-256, or 1: Use SHA-224.
*/
MBEDTLS_DEPRECATED void mbedtls_sha256_starts( mbedtls_sha256_context *ctx,
int is224 );
@@ -176,7 +182,7 @@ MBEDTLS_DEPRECATED void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
* \deprecated Superseded by mbedtls_sha256_finish_ret() in 2.7.0.
*
* \param ctx The SHA-256 context.
- * \param output The SHA-224or SHA-256 checksum result.
+ * \param output The SHA-224 or SHA-256 checksum result.
*/
MBEDTLS_DEPRECATED void mbedtls_sha256_finish( mbedtls_sha256_context *ctx,
unsigned char output[32] );
@@ -196,17 +202,6 @@ MBEDTLS_DEPRECATED void mbedtls_sha256_process( mbedtls_sha256_context *ctx,
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_SHA256_ALT */
-#include "sha256_alt.h"
-#endif /* MBEDTLS_SHA256_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
/**
* \brief This function calculates the SHA-224 or SHA-256
@@ -221,9 +216,8 @@ extern "C" {
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
* \param output The SHA-224 or SHA-256 checksum result.
- * \param is224 Determines which function to use.
- * <ul><li>0: Use SHA-256.</li>
- * <li>1: Use SHA-224.</li></ul>
+ * \param is224 Determines which function to use:
+ * 0: Use SHA-256, or 1: Use SHA-224.
*/
int mbedtls_sha256_ret( const unsigned char *input,
size_t ilen,
@@ -252,9 +246,8 @@ int mbedtls_sha256_ret( const unsigned char *input,
* \param input The buffer holding the data.
* \param ilen The length of the input data.
* \param output The SHA-224 or SHA-256 checksum result.
- * \param is224 Determines which function to use.
- * <ul><li>0: Use SHA-256.</li>
- * <li>1: Use SHA-224.</li></ul>
+ * \param is224 Determines which function to use:
+ * 0: Use SHA-256, or 1: Use SHA-224.
*/
MBEDTLS_DEPRECATED void mbedtls_sha256( const unsigned char *input,
size_t ilen,
@@ -267,7 +260,8 @@ MBEDTLS_DEPRECATED void mbedtls_sha256( const unsigned char *input,
/**
* \brief The SHA-224 and SHA-256 checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_sha256_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/sha512.h b/thirdparty/mbedtls/include/mbedtls/sha512.h
index 8404a2d599..5bb83f43bd 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha512.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha512.h
@@ -1,7 +1,9 @@
/**
* \file sha512.h
+ * \brief This file contains SHA-384 and SHA-512 definitions and functions.
*
- * \brief The SHA-384 and SHA-512 cryptographic hash function.
+ * The Secure Hash Algorithms 384 and 512 (SHA-384 and SHA-512) cryptographic
+ * hash functions are defined in <em>FIPS 180-4: Secure Hash Standard (SHS)</em>.
*/
/*
* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved
@@ -35,14 +37,14 @@
#define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED -0x0039 /**< SHA-512 hardware accelerator failed */
-#if !defined(MBEDTLS_SHA512_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_SHA512_ALT)
+// Regular implementation
+//
+
/**
* \brief The SHA-512 context structure.
*
@@ -55,12 +57,15 @@ typedef struct
uint64_t total[2]; /*!< The number of Bytes processed. */
uint64_t state[8]; /*!< The intermediate digest state. */
unsigned char buffer[128]; /*!< The data block being processed. */
- int is384; /*!< Determines which function to use.
- * <ul><li>0: Use SHA-512.</li>
- * <li>1: Use SHA-384.</li></ul> */
+ int is384; /*!< Determines which function to use:
+ 0: Use SHA-512, or 1: Use SHA-384. */
}
mbedtls_sha512_context;
+#else /* MBEDTLS_SHA512_ALT */
+#include "sha512_alt.h"
+#endif /* MBEDTLS_SHA512_ALT */
+
/**
* \brief This function initializes a SHA-512 context.
*
@@ -89,9 +94,8 @@ void mbedtls_sha512_clone( mbedtls_sha512_context *dst,
* calculation.
*
* \param ctx The SHA-512 context to initialize.
- * \param is384 Determines which function to use.
- * <ul><li>0: Use SHA-512.</li>
- * <li>1: Use SHA-384.</li></ul>
+ * \param is384 Determines which function to use:
+ * 0: Use SHA-512, or 1: Use SHA-384.
*
* \return \c 0 on success.
*/
@@ -148,9 +152,8 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx,
* \deprecated Superseded by mbedtls_sha512_starts_ret() in 2.7.0
*
* \param ctx The SHA-512 context to initialize.
- * \param is384 Determines which function to use.
- * <ul><li>0: Use SHA-512.</li>
- * <li>1: Use SHA-384.</li></ul>
+ * \param is384 Determines which function to use:
+ * 0: Use SHA-512, or 1: Use SHA-384.
*/
MBEDTLS_DEPRECATED void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
int is384 );
@@ -159,7 +162,7 @@ MBEDTLS_DEPRECATED void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
* \brief This function feeds an input buffer into an ongoing
* SHA-512 checksum calculation.
*
- * \deprecated Superseded by mbedtls_sha512_update_ret() in 2.7.0
+ * \deprecated Superseded by mbedtls_sha512_update_ret() in 2.7.0.
*
* \param ctx The SHA-512 context.
* \param input The buffer holding the data.
@@ -173,7 +176,7 @@ MBEDTLS_DEPRECATED void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
* \brief This function finishes the SHA-512 operation, and writes
* the result to the output buffer.
*
- * \deprecated Superseded by mbedtls_sha512_finish_ret() in 2.7.0
+ * \deprecated Superseded by mbedtls_sha512_finish_ret() in 2.7.0.
*
* \param ctx The SHA-512 context.
* \param output The SHA-384 or SHA-512 checksum result.
@@ -186,7 +189,7 @@ MBEDTLS_DEPRECATED void mbedtls_sha512_finish( mbedtls_sha512_context *ctx,
* the ongoing SHA-512 computation. This function is for
* internal use only.
*
- * \deprecated Superseded by mbedtls_internal_sha512_process() in 2.7.0
+ * \deprecated Superseded by mbedtls_internal_sha512_process() in 2.7.0.
*
* \param ctx The SHA-512 context.
* \param data The buffer holding one block of data.
@@ -198,18 +201,6 @@ MBEDTLS_DEPRECATED void mbedtls_sha512_process(
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_SHA512_ALT */
-#include "sha512_alt.h"
-#endif /* MBEDTLS_SHA512_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief This function calculates the SHA-512 or SHA-384
* checksum of a buffer.
@@ -223,9 +214,8 @@ extern "C" {
* \param input The buffer holding the input data.
* \param ilen The length of the input data.
* \param output The SHA-384 or SHA-512 checksum result.
- * \param is384 Determines which function to use.
- * <ul><li>0: Use SHA-512.</li>
- * <li>1: Use SHA-384.</li></ul>
+ * \param is384 Determines which function to use:
+ * 0: Use SHA-512, or 1: Use SHA-384.
*
* \return \c 0 on success.
*/
@@ -255,9 +245,8 @@ int mbedtls_sha512_ret( const unsigned char *input,
* \param input The buffer holding the data.
* \param ilen The length of the input data.
* \param output The SHA-384 or SHA-512 checksum result.
- * \param is384 Determines which function to use.
- * <ul><li>0: Use SHA-512.</li>
- * <li>1: Use SHA-384.</li></ul>
+ * \param is384 Determines which function to use:
+ * 0: Use SHA-512, or 1: Use SHA-384.
*/
MBEDTLS_DEPRECATED void mbedtls_sha512( const unsigned char *input,
size_t ilen,
@@ -269,7 +258,8 @@ MBEDTLS_DEPRECATED void mbedtls_sha512( const unsigned char *input,
/**
* \brief The SHA-384 or SHA-512 checkup routine.
*
- * \return \c 0 on success, or \c 1 on failure.
+ * \return \c 0 on success.
+ * \return \c 1 on failure.
*/
int mbedtls_sha512_self_test( int verbose );
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h
index dffc162191..250031a6d3 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl.h
@@ -112,13 +112,14 @@
#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80 /**< DTLS client must retry for hello verification */
#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00 /**< A buffer is too small to receive or write a message */
#define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980 /**< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */
-#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< Connection requires a read call. */
+#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< No data of requested type currently available on underlying transport. */
#define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /**< Connection requires a write call. */
#define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /**< The operation timed out. */
#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /**< The client initiated a reconnect from the same port. */
#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 /**< Record header looks valid but is not expected. */
#define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 /**< The alert message received indicates a non-fatal error. */
#define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600 /**< Couldn't set the hash for verifying CertificateVerify */
+#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580 /**< Internal-only message signaling that further message-processing should be done */
/*
* Various constants
@@ -682,10 +683,18 @@ struct mbedtls_ssl_config
#endif
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
- unsigned char *psk; /*!< pre-shared key */
- size_t psk_len; /*!< length of the pre-shared key */
- unsigned char *psk_identity; /*!< identity for PSK negotiation */
- size_t psk_identity_len;/*!< length of identity */
+ unsigned char *psk; /*!< pre-shared key. This field should
+ only be set via
+ mbedtls_ssl_conf_psk() */
+ size_t psk_len; /*!< length of the pre-shared key. This
+ field should only be set via
+ mbedtls_ssl_conf_psk() */
+ unsigned char *psk_identity; /*!< identity for PSK negotiation. This
+ field should only be set via
+ mbedtls_ssl_conf_psk() */
+ size_t psk_identity_len;/*!< length of identity. This field should
+ only be set via
+ mbedtls_ssl_conf_psk() */
#endif
#if defined(MBEDTLS_SSL_ALPN)
@@ -938,14 +947,6 @@ extern int (*mbedtls_ssl_hw_record_finish)(mbedtls_ssl_context *ssl);
#endif /* MBEDTLS_SSL_HW_RECORD_ACCEL */
/**
- * \brief Returns the list of ciphersuites supported by the SSL/TLS module.
- *
- * \return a statically allocated array of ciphersuites, the last
- * entry is 0.
- */
-const int *mbedtls_ssl_list_ciphersuites( void );
-
-/**
* \brief Return the name of the ciphersuite associated with the
* given ID
*
@@ -1601,6 +1602,10 @@ void mbedtls_ssl_conf_cert_profile( mbedtls_ssl_config *conf,
/**
* \brief Set the data required to verify peer certificate
*
+ * \note See \c mbedtls_x509_crt_verify() for notes regarding the
+ * parameters ca_chain (maps to trust_ca for that function)
+ * and ca_crl.
+ *
* \param conf SSL configuration
* \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs)
* \param ca_crl trusted CA CRLs
@@ -1841,21 +1846,21 @@ void mbedtls_ssl_conf_sig_hashes( mbedtls_ssl_config *conf,
#if defined(MBEDTLS_X509_CRT_PARSE_C)
/**
- * \brief Set or reset the hostname to check against the received
- * server certificate. It sets the ServerName TLS extension,
+ * \brief Set or reset the hostname to check against the received
+ * server certificate. It sets the ServerName TLS extension,
* too, if that extension is enabled. (client-side only)
*
* \param ssl SSL context
* \param hostname the server hostname, may be NULL to clear hostname
-
+
* \note Maximum hostname length MBEDTLS_SSL_MAX_HOST_NAME_LEN.
*
- * \return 0 if successful, MBEDTLS_ERR_SSL_ALLOC_FAILED on
- * allocation failure, MBEDTLS_ERR_SSL_BAD_INPUT_DATA on
+ * \return 0 if successful, MBEDTLS_ERR_SSL_ALLOC_FAILED on
+ * allocation failure, MBEDTLS_ERR_SSL_BAD_INPUT_DATA on
* too long input hostname.
*
* Hostname set to the one provided on success (cleared
- * when NULL). On allocation failure hostname is cleared.
+ * when NULL). On allocation failure hostname is cleared.
* On too long input failure, old hostname is unchanged.
*/
int mbedtls_ssl_set_hostname( mbedtls_ssl_context *ssl, const char *hostname );
@@ -2289,11 +2294,59 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf,
#endif /* MBEDTLS_SSL_RENEGOTIATION */
/**
- * \brief Return the number of data bytes available to read
+ * \brief Check if there is data already read from the
+ * underlying transport but not yet processed.
+ *
+ * \param ssl SSL context
+ *
+ * \return 0 if nothing's pending, 1 otherwise.
+ *
+ * \note This is different in purpose and behaviour from
+ * \c mbedtls_ssl_get_bytes_avail in that it considers
+ * any kind of unprocessed data, not only unread
+ * application data. If \c mbedtls_ssl_get_bytes
+ * returns a non-zero value, this function will
+ * also signal pending data, but the converse does
+ * not hold. For example, in DTLS there might be
+ * further records waiting to be processed from
+ * the current underlying transport's datagram.
+ *
+ * \note If this function returns 1 (data pending), this
+ * does not imply that a subsequent call to
+ * \c mbedtls_ssl_read will provide any data;
+ * e.g., the unprocessed data might turn out
+ * to be an alert or a handshake message.
+ *
+ * \note This function is useful in the following situation:
+ * If the SSL/TLS module successfully returns from an
+ * operation - e.g. a handshake or an application record
+ * read - and you're awaiting incoming data next, you
+ * must not immediately idle on the underlying transport
+ * to have data ready, but you need to check the value
+ * of this function first. The reason is that the desired
+ * data might already be read but not yet processed.
+ * If, in contrast, a previous call to the SSL/TLS module
+ * returned MBEDTLS_ERR_SSL_WANT_READ, it is not necessary
+ * to call this function, as the latter error code entails
+ * that all internal data has been processed.
+ *
+ */
+int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl );
+
+/**
+ * \brief Return the number of application data bytes
+ * remaining to be read from the current record.
*
* \param ssl SSL context
*
- * \return how many bytes are available in the read buffer
+ * \return How many bytes are available in the application
+ * data record read buffer.
+ *
+ * \note When working over a datagram transport, this is
+ * useful to detect the current datagram's boundary
+ * in case \c mbedtls_ssl_read has written the maximal
+ * amount of data fitting into the input buffer.
+ *
*/
size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl );
@@ -2408,11 +2461,25 @@ int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED (see below), or
* a specific SSL error code.
*
+ * If this function returns MBEDTLS_ERR_SSL_WANT_READ, the
+ * handshake is unfinished and no further data is available
+ * from the underlying transport. In this case, you must call
+ * the function again at some later stage.
+ *
+ * \note Remarks regarding event-driven DTLS:
+ * If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
+ * from the underlying transport layer is currently being processed,
+ * and it is safe to idle until the timer or the underlying transport
+ * signal a new event. This is not true for a successful handshake,
+ * in which case the datagram of the underlying transport that is
+ * currently being processed might or might not contain further
+ * DTLS records.
+ *
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \note If DTLS is in use, then you may choose to handle
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging
@@ -2429,10 +2496,10 @@ int mbedtls_ssl_handshake( mbedtls_ssl_context *ssl );
* call this function if state is MBEDTLS_SSL_HANDSHAKE_OVER.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \param ssl SSL context
*
@@ -2456,10 +2523,10 @@ int mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl );
* value.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_SSL_RENEGOTIATION */
@@ -2471,20 +2538,20 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
* \param buf buffer that will hold the data
* \param len maximum number of bytes to read
*
- * \return the number of bytes read, or
- * 0 for EOF, or
- * MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or
- * MBEDTLS_ERR_SSL_CLIENT_RECONNECT (see below), or
- * another negative error code.
+ * \return One of the following:
+ * - 0 if the read end of the underlying transport was closed,
+ * - the (positive) number of bytes read, or
+ * - a negative error code on failure.
*
- * \note If this function returns something other than a positive
- * value or MBEDTLS_ERR_SSL_WANT_READ/WRITE or
- * MBEDTLS_ERR_SSL_CLIENT_RECONNECT, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * If MBEDTLS_ERR_SSL_WANT_READ is returned, no application data
+ * is available from the underlying transport. In this case,
+ * the function needs to be called again at some later stage.
*
- * \note When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
+ * If MBEDTLS_ERR_SSL_WANT_WRITE is returned, a write is pending
+ * but the underlying transport isn't available for writing. In this
+ * case, the function needs to be called again at some later stage.
+ *
+ * When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
* (which can only happen server-side), it means that a client
* is initiating a new connection using the same source port.
* You can either treat that as a connection close and wait
@@ -2497,6 +2564,28 @@ int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
* again. WARNING: not validating the identity of the client
* again, or not transmitting the new identity to the
* application layer, would allow authentication bypass!
+ *
+ * \note If this function returns something other than a positive value
+ * or MBEDTLS_ERR_SSL_WANT_READ/WRITE or MBEDTLS_ERR_SSL_CLIENT_RECONNECT,
+ * you must stop using the SSL context for reading or writing,
+ * and either free it or call \c mbedtls_ssl_session_reset() on it
+ * before re-using it for a new connection; the current connection
+ * must be closed.
+ *
+ * \note Remarks regarding event-driven DTLS:
+ * - If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
+ * from the underlying transport layer is currently being processed,
+ * and it is safe to idle until the timer or the underlying transport
+ * signal a new event.
+ * - This function may return MBEDTLS_ERR_SSL_WANT_READ even if data was
+ * initially available on the underlying transport, as this data may have
+ * been only e.g. duplicated messages or a renegotiation request.
+ * Therefore, you must be prepared to receive MBEDTLS_ERR_SSL_WANT_READ even
+ * when reacting to an incoming-data event from the underlying transport.
+ * - On success, the datagram of the underlying transport that is currently
+ * being processed may contain further DTLS records. You should call
+ * \c mbedtls_ssl_check_pending to check for remaining records.
+ *
*/
int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len );
@@ -2517,15 +2606,17 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
* or MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ,
* or another negative error code.
*
- * \note If this function returns something other than a positive
- * value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * \note If this function returns something other than a positive value
+ * or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ,
* it must be called later with the *same* arguments,
- * until it returns a positive value.
+ * until it returns a positive value. When the function returns
+ * MBEDTLS_ERR_SSL_WANT_WRITE there may be some partial
+ * data in the output buffer, however this is not yet sent.
*
* \note If the requested length is greater than the maximum
* fragment length (either the built-in limit or the one set
@@ -2548,10 +2639,10 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_
* \return 0 if successful, or a specific SSL error code.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl,
unsigned char level,
@@ -2564,10 +2655,10 @@ int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl,
* \return 0 if successful, or a specific SSL error code.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl );
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h b/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h
index 545468a510..7d5eba0916 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h
@@ -169,6 +169,45 @@ extern "C" {
#define MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A /**< Weak! No SSL3! */
#define MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B /**< Weak! No SSL3! */
+#define MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 0xC03C /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 0xC03D /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC044 /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC045 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC048 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC049 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC04A /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC04B /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC04C /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC04D /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 0xC04E /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 0xC04F /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 0xC050 /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 0xC051 /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC052 /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC053 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05C /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05D /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05E /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05F /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC060 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC061 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 0xC062 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 0xC063 /**< TLS 1.2 */
+#define MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 0xC064 /**< TLS 1.2 */
+#define MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 0xC065 /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC066 /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC067 /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 0xC068 /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 0xC069 /**< TLS 1.2 */
+#define MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 0xC06A /**< TLS 1.2 */
+#define MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 0xC06B /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 0xC06C /**< TLS 1.2 */
+#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 0xC06D /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0xC06E /**< TLS 1.2 */
+#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0xC06F /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC070 /**< TLS 1.2 */
+#define MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC071 /**< TLS 1.2 */
+
#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072 /**< Not in SSL3! */
#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073 /**< Not in SSL3! */
#define MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074 /**< Not in SSL3! */
@@ -267,7 +306,7 @@ typedef enum {
defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \
defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) || \
- defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+ defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
#define MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED
#endif
diff --git a/thirdparty/mbedtls/include/mbedtls/threading.h b/thirdparty/mbedtls/include/mbedtls/threading.h
index 58e6db2f3a..aeea5d0e1a 100644
--- a/thirdparty/mbedtls/include/mbedtls/threading.h
+++ b/thirdparty/mbedtls/include/mbedtls/threading.h
@@ -96,8 +96,12 @@ extern int (*mbedtls_mutex_unlock)( mbedtls_threading_mutex_t *mutex );
/*
* Global mutexes
*/
+#if defined(MBEDTLS_FS_IO)
extern mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex;
+#endif
+#if defined(MBEDTLS_HAVE_TIME_DATE)
extern mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex;
+#endif
#endif /* MBEDTLS_THREADING_C */
#ifdef __cplusplus
diff --git a/thirdparty/mbedtls/include/mbedtls/timing.h b/thirdparty/mbedtls/include/mbedtls/timing.h
index 2c497bf4eb..bbcb90688a 100644
--- a/thirdparty/mbedtls/include/mbedtls/timing.h
+++ b/thirdparty/mbedtls/include/mbedtls/timing.h
@@ -30,16 +30,16 @@
#include MBEDTLS_CONFIG_FILE
#endif
-#if !defined(MBEDTLS_TIMING_ALT)
-// Regular implementation
-//
-
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_TIMING_ALT)
+// Regular implementation
+//
+
/**
* \brief timer structure
*/
@@ -58,6 +58,10 @@ typedef struct
uint32_t fin_ms;
} mbedtls_timing_delay_context;
+#else /* MBEDTLS_TIMING_ALT */
+#include "timing_alt.h"
+#endif /* MBEDTLS_TIMING_ALT */
+
extern volatile int mbedtls_timing_alarmed;
/**
@@ -133,18 +137,6 @@ void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms );
*/
int mbedtls_timing_get_delay( void *data );
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_TIMING_ALT */
-#include "timing_alt.h"
-#endif /* MBEDTLS_TIMING_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#if defined(MBEDTLS_SELF_TEST)
/**
* \brief Checkup routine
diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h
index c3ee649f5c..83e3c1726b 100644
--- a/thirdparty/mbedtls/include/mbedtls/version.h
+++ b/thirdparty/mbedtls/include/mbedtls/version.h
@@ -39,7 +39,7 @@
* Major, Minor, Patchlevel
*/
#define MBEDTLS_VERSION_MAJOR 2
-#define MBEDTLS_VERSION_MINOR 8
+#define MBEDTLS_VERSION_MINOR 10
#define MBEDTLS_VERSION_PATCH 0
/**
@@ -47,9 +47,9 @@
* MMNNPP00
* Major version | Minor version | Patch version
*/
-#define MBEDTLS_VERSION_NUMBER 0x02080000
-#define MBEDTLS_VERSION_STRING "2.8.0"
-#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.8.0"
+#define MBEDTLS_VERSION_NUMBER 0x020A0000
+#define MBEDTLS_VERSION_STRING "2.10.0"
+#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.10.0"
#if defined(MBEDTLS_VERSION_C)
diff --git a/thirdparty/mbedtls/include/mbedtls/x509_crt.h b/thirdparty/mbedtls/include/mbedtls/x509_crt.h
index 2dbb7ec964..ac23cffe84 100644
--- a/thirdparty/mbedtls/include/mbedtls/x509_crt.h
+++ b/thirdparty/mbedtls/include/mbedtls/x509_crt.h
@@ -287,8 +287,15 @@ int mbedtls_x509_crt_verify_info( char *buf, size_t size, const char *prefix,
* used to sign the certificate, CRL verification is skipped
* silently, that is *without* setting any flag.
*
+ * \note The \c trust_ca list can contain two types of certificates:
+ * (1) those of trusted root CAs, so that certificates
+ * chaining up to those CAs will be trusted, and (2)
+ * self-signed end-entity certificates to be trusted (for
+ * specific peers you know) - in that case, the self-signed
+ * certificate doesn't need to have the CA bit set.
+ *
* \param crt a certificate (chain) to be verified
- * \param trust_ca the list of trusted CAs
+ * \param trust_ca the list of trusted CAs (see note above)
* \param ca_crl the list of CRLs for trusted CAs (see note above)
* \param cn expected Common Name (can be set to
* NULL if the CN must not be verified)
diff --git a/thirdparty/mbedtls/include/mbedtls/xtea.h b/thirdparty/mbedtls/include/mbedtls/xtea.h
index 34ccee3c22..8df708a3a5 100644
--- a/thirdparty/mbedtls/include/mbedtls/xtea.h
+++ b/thirdparty/mbedtls/include/mbedtls/xtea.h
@@ -39,14 +39,14 @@
#define MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH -0x0028 /**< The data input has an invalid length. */
#define MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED -0x0029 /**< XTEA hardware accelerator failed. */
-#if !defined(MBEDTLS_XTEA_ALT)
-// Regular implementation
-//
-
#ifdef __cplusplus
extern "C" {
#endif
+#if !defined(MBEDTLS_XTEA_ALT)
+// Regular implementation
+//
+
/**
* \brief XTEA context structure
*/
@@ -56,6 +56,10 @@ typedef struct
}
mbedtls_xtea_context;
+#else /* MBEDTLS_XTEA_ALT */
+#include "xtea_alt.h"
+#endif /* MBEDTLS_XTEA_ALT */
+
/**
* \brief Initialize XTEA context
*
@@ -115,18 +119,6 @@ int mbedtls_xtea_crypt_cbc( mbedtls_xtea_context *ctx,
unsigned char *output);
#endif /* MBEDTLS_CIPHER_MODE_CBC */
-#ifdef __cplusplus
-}
-#endif
-
-#else /* MBEDTLS_XTEA_ALT */
-#include "xtea_alt.h"
-#endif /* MBEDTLS_XTEA_ALT */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* \brief Checkup routine
*
diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c
index 3d2eac82dd..fea9b5383d 100644
--- a/thirdparty/mbedtls/library/aes.c
+++ b/thirdparty/mbedtls/library/aes.c
@@ -36,6 +36,7 @@
#include <string.h>
#include "mbedtls/aes.h"
+#include "mbedtls/platform_util.h"
#if defined(MBEDTLS_PADLOCK_C)
#include "mbedtls/padlock.h"
#endif
@@ -54,11 +55,6 @@
#if !defined(MBEDTLS_AES_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (little endian)
*/
@@ -201,6 +197,8 @@ static const unsigned char FSb[256] =
static const uint32_t FT0[256] = { FT };
#undef V
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
+
#define V(a,b,c,d) 0x##b##c##d##a
static const uint32_t FT1[256] = { FT };
#undef V
@@ -213,6 +211,8 @@ static const uint32_t FT2[256] = { FT };
static const uint32_t FT3[256] = { FT };
#undef V
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
+
#undef FT
/*
@@ -328,6 +328,8 @@ static const unsigned char RSb[256] =
static const uint32_t RT0[256] = { RT };
#undef V
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
+
#define V(a,b,c,d) 0x##b##c##d##a
static const uint32_t RT1[256] = { RT };
#undef V
@@ -340,6 +342,8 @@ static const uint32_t RT2[256] = { RT };
static const uint32_t RT3[256] = { RT };
#undef V
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
+
#undef RT
/*
@@ -359,18 +363,22 @@ static const uint32_t RCON[10] =
*/
static unsigned char FSb[256];
static uint32_t FT0[256];
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
static uint32_t FT1[256];
static uint32_t FT2[256];
static uint32_t FT3[256];
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
/*
* Reverse S-box & tables
*/
static unsigned char RSb[256];
static uint32_t RT0[256];
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
static uint32_t RT1[256];
static uint32_t RT2[256];
static uint32_t RT3[256];
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
/*
* Round constants
@@ -445,9 +453,11 @@ static void aes_gen_tables( void )
( (uint32_t) x << 16 ) ^
( (uint32_t) z << 24 );
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
FT1[i] = ROTL8( FT0[i] );
FT2[i] = ROTL8( FT1[i] );
FT3[i] = ROTL8( FT2[i] );
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
x = RSb[i];
@@ -456,14 +466,48 @@ static void aes_gen_tables( void )
( (uint32_t) MUL( 0x0D, x ) << 16 ) ^
( (uint32_t) MUL( 0x0B, x ) << 24 );
+#if !defined(MBEDTLS_AES_FEWER_TABLES)
RT1[i] = ROTL8( RT0[i] );
RT2[i] = ROTL8( RT1[i] );
RT3[i] = ROTL8( RT2[i] );
+#endif /* !MBEDTLS_AES_FEWER_TABLES */
}
}
+#undef ROTL8
+
#endif /* MBEDTLS_AES_ROM_TABLES */
+#if defined(MBEDTLS_AES_FEWER_TABLES)
+
+#define ROTL8(x) ( (uint32_t)( ( x ) << 8 ) + (uint32_t)( ( x ) >> 24 ) )
+#define ROTL16(x) ( (uint32_t)( ( x ) << 16 ) + (uint32_t)( ( x ) >> 16 ) )
+#define ROTL24(x) ( (uint32_t)( ( x ) << 24 ) + (uint32_t)( ( x ) >> 8 ) )
+
+#define AES_RT0(idx) RT0[idx]
+#define AES_RT1(idx) ROTL8( RT0[idx] )
+#define AES_RT2(idx) ROTL16( RT0[idx] )
+#define AES_RT3(idx) ROTL24( RT0[idx] )
+
+#define AES_FT0(idx) FT0[idx]
+#define AES_FT1(idx) ROTL8( FT0[idx] )
+#define AES_FT2(idx) ROTL16( FT0[idx] )
+#define AES_FT3(idx) ROTL24( FT0[idx] )
+
+#else /* MBEDTLS_AES_FEWER_TABLES */
+
+#define AES_RT0(idx) RT0[idx]
+#define AES_RT1(idx) RT1[idx]
+#define AES_RT2(idx) RT2[idx]
+#define AES_RT3(idx) RT3[idx]
+
+#define AES_FT0(idx) FT0[idx]
+#define AES_FT1(idx) FT1[idx]
+#define AES_FT2(idx) FT2[idx]
+#define AES_FT3(idx) FT3[idx]
+
+#endif /* MBEDTLS_AES_FEWER_TABLES */
+
void mbedtls_aes_init( mbedtls_aes_context *ctx )
{
memset( ctx, 0, sizeof( mbedtls_aes_context ) );
@@ -474,7 +518,7 @@ void mbedtls_aes_free( mbedtls_aes_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_aes_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_aes_context ) );
}
/*
@@ -641,10 +685,10 @@ int mbedtls_aes_setkey_dec( mbedtls_aes_context *ctx, const unsigned char *key,
{
for( j = 0; j < 4; j++, SK++ )
{
- *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^
- RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^
- RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^
- RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ];
+ *RK++ = AES_RT0( FSb[ ( *SK ) & 0xFF ] ) ^
+ AES_RT1( FSb[ ( *SK >> 8 ) & 0xFF ] ) ^
+ AES_RT2( FSb[ ( *SK >> 16 ) & 0xFF ] ) ^
+ AES_RT3( FSb[ ( *SK >> 24 ) & 0xFF ] );
}
}
@@ -660,50 +704,50 @@ exit:
}
#endif /* !MBEDTLS_AES_SETKEY_DEC_ALT */
-#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
-{ \
- X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \
- FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
- FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
- FT3[ ( Y3 >> 24 ) & 0xFF ]; \
- \
- X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \
- FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
- FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
- FT3[ ( Y0 >> 24 ) & 0xFF ]; \
- \
- X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \
- FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
- FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
- FT3[ ( Y1 >> 24 ) & 0xFF ]; \
- \
- X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \
- FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
- FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
- FT3[ ( Y2 >> 24 ) & 0xFF ]; \
+#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
+{ \
+ X0 = *RK++ ^ AES_FT0( ( Y0 ) & 0xFF ) ^ \
+ AES_FT1( ( Y1 >> 8 ) & 0xFF ) ^ \
+ AES_FT2( ( Y2 >> 16 ) & 0xFF ) ^ \
+ AES_FT3( ( Y3 >> 24 ) & 0xFF ); \
+ \
+ X1 = *RK++ ^ AES_FT0( ( Y1 ) & 0xFF ) ^ \
+ AES_FT1( ( Y2 >> 8 ) & 0xFF ) ^ \
+ AES_FT2( ( Y3 >> 16 ) & 0xFF ) ^ \
+ AES_FT3( ( Y0 >> 24 ) & 0xFF ); \
+ \
+ X2 = *RK++ ^ AES_FT0( ( Y2 ) & 0xFF ) ^ \
+ AES_FT1( ( Y3 >> 8 ) & 0xFF ) ^ \
+ AES_FT2( ( Y0 >> 16 ) & 0xFF ) ^ \
+ AES_FT3( ( Y1 >> 24 ) & 0xFF ); \
+ \
+ X3 = *RK++ ^ AES_FT0( ( Y3 ) & 0xFF ) ^ \
+ AES_FT1( ( Y0 >> 8 ) & 0xFF ) ^ \
+ AES_FT2( ( Y1 >> 16 ) & 0xFF ) ^ \
+ AES_FT3( ( Y2 >> 24 ) & 0xFF ); \
}
-#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
-{ \
- X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \
- RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
- RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
- RT3[ ( Y1 >> 24 ) & 0xFF ]; \
- \
- X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \
- RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
- RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
- RT3[ ( Y2 >> 24 ) & 0xFF ]; \
- \
- X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \
- RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
- RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
- RT3[ ( Y3 >> 24 ) & 0xFF ]; \
- \
- X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \
- RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
- RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
- RT3[ ( Y0 >> 24 ) & 0xFF ]; \
+#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
+{ \
+ X0 = *RK++ ^ AES_RT0( ( Y0 ) & 0xFF ) ^ \
+ AES_RT1( ( Y3 >> 8 ) & 0xFF ) ^ \
+ AES_RT2( ( Y2 >> 16 ) & 0xFF ) ^ \
+ AES_RT3( ( Y1 >> 24 ) & 0xFF ); \
+ \
+ X1 = *RK++ ^ AES_RT0( ( Y1 ) & 0xFF ) ^ \
+ AES_RT1( ( Y0 >> 8 ) & 0xFF ) ^ \
+ AES_RT2( ( Y3 >> 16 ) & 0xFF ) ^ \
+ AES_RT3( ( Y2 >> 24 ) & 0xFF ); \
+ \
+ X2 = *RK++ ^ AES_RT0( ( Y2 ) & 0xFF ) ^ \
+ AES_RT1( ( Y1 >> 8 ) & 0xFF ) ^ \
+ AES_RT2( ( Y0 >> 16 ) & 0xFF ) ^ \
+ AES_RT3( ( Y3 >> 24 ) & 0xFF ); \
+ \
+ X3 = *RK++ ^ AES_RT0( ( Y3 ) & 0xFF ) ^ \
+ AES_RT1( ( Y2 >> 8 ) & 0xFF ) ^ \
+ AES_RT2( ( Y1 >> 16 ) & 0xFF ) ^ \
+ AES_RT3( ( Y0 >> 24 ) & 0xFF ); \
}
/*
@@ -1034,6 +1078,9 @@ int mbedtls_aes_crypt_ctr( mbedtls_aes_context *ctx,
int c, i;
size_t n = *nc_off;
+ if ( n > 0x0F )
+ return( MBEDTLS_ERR_AES_BAD_INPUT_DATA );
+
while( length-- )
{
if( n == 0 ) {
diff --git a/thirdparty/mbedtls/library/aesni.c b/thirdparty/mbedtls/library/aesni.c
index 1ca3c3ef5b..062708b047 100644
--- a/thirdparty/mbedtls/library/aesni.c
+++ b/thirdparty/mbedtls/library/aesni.c
@@ -32,6 +32,12 @@
#if defined(MBEDTLS_AESNI_C)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#warning "MBEDTLS_AESNI_C is known to cause spurious error reports with some memory sanitizers as they do not understand the assembly code."
+#endif
+#endif
+
#include "mbedtls/aesni.h"
#include <string.h>
diff --git a/thirdparty/mbedtls/library/arc4.c b/thirdparty/mbedtls/library/arc4.c
index 05b33d3fdb..b8998ac6cd 100644
--- a/thirdparty/mbedtls/library/arc4.c
+++ b/thirdparty/mbedtls/library/arc4.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_ARC4_C)
#include "mbedtls/arc4.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -47,11 +48,6 @@
#if !defined(MBEDTLS_ARC4_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
void mbedtls_arc4_init( mbedtls_arc4_context *ctx )
{
memset( ctx, 0, sizeof( mbedtls_arc4_context ) );
@@ -62,7 +58,7 @@ void mbedtls_arc4_free( mbedtls_arc4_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_arc4_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_arc4_context ) );
}
/*
diff --git a/thirdparty/mbedtls/library/aria.c b/thirdparty/mbedtls/library/aria.c
new file mode 100644
index 0000000000..e9bcd6d135
--- /dev/null
+++ b/thirdparty/mbedtls/library/aria.c
@@ -0,0 +1,1028 @@
+/*
+ * ARIA implementation
+ *
+ * Copyright (C) 2006-2017, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/*
+ * This implementation is based on the following standards:
+ * [1] http://210.104.33.10/ARIA/doc/ARIA-specification-e.pdf
+ * [2] https://tools.ietf.org/html/rfc5794
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_ARIA_C)
+
+#include "mbedtls/aria.h"
+
+#include <string.h>
+
+#if defined(MBEDTLS_SELF_TEST)
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdio.h>
+#define mbedtls_printf printf
+#endif /* MBEDTLS_PLATFORM_C */
+#endif /* MBEDTLS_SELF_TEST */
+
+#if !defined(MBEDTLS_ARIA_ALT)
+
+#include "mbedtls/platform_util.h"
+
+#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+ !defined(inline) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_UINT32_LE
+#define GET_UINT32_LE( n, b, i ) \
+{ \
+ (n) = ( (uint32_t) (b)[(i) ] ) \
+ | ( (uint32_t) (b)[(i) + 1] << 8 ) \
+ | ( (uint32_t) (b)[(i) + 2] << 16 ) \
+ | ( (uint32_t) (b)[(i) + 3] << 24 ); \
+}
+#endif
+
+#ifndef PUT_UINT32_LE
+#define PUT_UINT32_LE( n, b, i ) \
+{ \
+ (b)[(i) ] = (unsigned char) ( ( (n) ) & 0xFF ); \
+ (b)[(i) + 1] = (unsigned char) ( ( (n) >> 8 ) & 0xFF ); \
+ (b)[(i) + 2] = (unsigned char) ( ( (n) >> 16 ) & 0xFF ); \
+ (b)[(i) + 3] = (unsigned char) ( ( (n) >> 24 ) & 0xFF ); \
+}
+#endif
+
+/*
+ * modify byte order: ( A B C D ) -> ( B A D C ), i.e. swap pairs of bytes
+ *
+ * This is submatrix P1 in [1] Appendix B.1
+ *
+ * Common compilers fail to translate this to minimal number of instructions,
+ * so let's provide asm versions for common platforms with C fallback.
+ */
+#if defined(MBEDTLS_HAVE_ASM)
+#if defined(__arm__) /* rev16 available from v6 up */
+/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */
+#if defined(__GNUC__) && \
+ ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) && \
+ __ARM_ARCH >= 6
+static inline uint32_t aria_p1( uint32_t x )
+{
+ uint32_t r;
+ __asm( "rev16 %0, %1" : "=l" (r) : "l" (x) );
+ return( r );
+}
+#define ARIA_P1 aria_p1
+#elif defined(__ARMCC_VERSION) && __ARMCC_VERSION < 6000000 && \
+ ( __TARGET_ARCH_ARM >= 6 || __TARGET_ARCH_THUMB >= 3 )
+static inline uint32_t aria_p1( uint32_t x )
+{
+ uint32_t r;
+ __asm( "rev16 r, x" );
+ return( r );
+}
+#define ARIA_P1 aria_p1
+#endif
+#endif /* arm */
+#if defined(__GNUC__) && \
+ defined(__i386__) || defined(__amd64__) || defined( __x86_64__)
+/* I couldn't find an Intel equivalent of rev16, so two instructions */
+#define ARIA_P1(x) ARIA_P2( ARIA_P3( x ) )
+#endif /* x86 gnuc */
+#endif /* MBEDTLS_HAVE_ASM && GNUC */
+#if !defined(ARIA_P1)
+#define ARIA_P1(x) ((((x) >> 8) & 0x00FF00FF) ^ (((x) & 0x00FF00FF) << 8))
+#endif
+
+/*
+ * modify byte order: ( A B C D ) -> ( C D A B ), i.e. rotate by 16 bits
+ *
+ * This is submatrix P2 in [1] Appendix B.1
+ *
+ * Common compilers will translate this to a single instruction.
+ */
+#define ARIA_P2(x) (((x) >> 16) ^ ((x) << 16))
+
+/*
+ * modify byte order: ( A B C D ) -> ( D C B A ), i.e. change endianness
+ *
+ * This is submatrix P3 in [1] Appendix B.1
+ *
+ * Some compilers fail to translate this to a single instruction,
+ * so let's provide asm versions for common platforms with C fallback.
+ */
+#if defined(MBEDTLS_HAVE_ASM)
+#if defined(__arm__) /* rev available from v6 up */
+/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */
+#if defined(__GNUC__) && \
+ ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) && \
+ __ARM_ARCH >= 6
+static inline uint32_t aria_p3( uint32_t x )
+{
+ uint32_t r;
+ __asm( "rev %0, %1" : "=l" (r) : "l" (x) );
+ return( r );
+}
+#define ARIA_P3 aria_p3
+#elif defined(__ARMCC_VERSION) && __ARMCC_VERSION < 6000000 && \
+ ( __TARGET_ARCH_ARM >= 6 || __TARGET_ARCH_THUMB >= 3 )
+static inline uint32_t aria_p3( uint32_t x )
+{
+ uint32_t r;
+ __asm( "rev r, x" );
+ return( r );
+}
+#define ARIA_P3 aria_p3
+#endif
+#endif /* arm */
+#if defined(__GNUC__) && \
+ defined(__i386__) || defined(__amd64__) || defined( __x86_64__)
+static inline uint32_t aria_p3( uint32_t x )
+{
+ __asm( "bswap %0" : "=r" (x) : "0" (x) );
+ return( x );
+}
+#define ARIA_P3 aria_p3
+#endif /* x86 gnuc */
+#endif /* MBEDTLS_HAVE_ASM && GNUC */
+#if !defined(ARIA_P3)
+#define ARIA_P3(x) ARIA_P2( ARIA_P1 ( x ) )
+#endif
+
+/*
+ * ARIA Affine Transform
+ * (a, b, c, d) = state in/out
+ *
+ * If we denote the first byte of input by 0, ..., the last byte by f,
+ * then inputs are: a = 0123, b = 4567, c = 89ab, d = cdef.
+ *
+ * Reading [1] 2.4 or [2] 2.4.3 in columns and performing simple
+ * rearrangements on adjacent pairs, output is:
+ *
+ * a = 3210 + 4545 + 6767 + 88aa + 99bb + dccd + effe
+ * = 3210 + 4567 + 6745 + 89ab + 98ba + dcfe + efcd
+ * b = 0101 + 2323 + 5476 + 8998 + baab + eecc + ffdd
+ * = 0123 + 2301 + 5476 + 89ab + ba98 + efcd + fedc
+ * c = 0022 + 1133 + 4554 + 7667 + ab89 + dcdc + fefe
+ * = 0123 + 1032 + 4567 + 7654 + ab89 + dcfe + fedc
+ * d = 1001 + 2332 + 6644 + 7755 + 9898 + baba + cdef
+ * = 1032 + 2301 + 6745 + 7654 + 98ba + ba98 + cdef
+ *
+ * Note: another presentation of the A transform can be found as the first
+ * half of App. B.1 in [1] in terms of 4-byte operators P1, P2, P3 and P4.
+ * The implementation below uses only P1 and P2 as they are sufficient.
+ */
+static inline void aria_a( uint32_t *a, uint32_t *b,
+ uint32_t *c, uint32_t *d )
+{
+ uint32_t ta, tb, tc;
+ ta = *b; // 4567
+ *b = *a; // 0123
+ *a = ARIA_P2( ta ); // 6745
+ tb = ARIA_P2( *d ); // efcd
+ *d = ARIA_P1( *c ); // 98ba
+ *c = ARIA_P1( tb ); // fedc
+ ta ^= *d; // 4567+98ba
+ tc = ARIA_P2( *b ); // 2301
+ ta = ARIA_P1( ta ) ^ tc ^ *c; // 2301+5476+89ab+fedc
+ tb ^= ARIA_P2( *d ); // ba98+efcd
+ tc ^= ARIA_P1( *a ); // 2301+7654
+ *b ^= ta ^ tb; // 0123+2301+5476+89ab+ba98+efcd+fedc OUT
+ tb = ARIA_P2( tb ) ^ ta; // 2301+5476+89ab+98ba+cdef+fedc
+ *a ^= ARIA_P1( tb ); // 3210+4567+6745+89ab+98ba+dcfe+efcd OUT
+ ta = ARIA_P2( ta ); // 0123+7654+ab89+dcfe
+ *d ^= ARIA_P1( ta ) ^ tc; // 1032+2301+6745+7654+98ba+ba98+cdef OUT
+ tc = ARIA_P2( tc ); // 0123+5476
+ *c ^= ARIA_P1( tc ) ^ ta; // 0123+1032+4567+7654+ab89+dcfe+fedc OUT
+}
+
+/*
+ * ARIA Substitution Layer SL1 / SL2
+ * (a, b, c, d) = state in/out
+ * (sa, sb, sc, sd) = 256 8-bit S-Boxes (see below)
+ *
+ * By passing sb1, sb2, is1, is2 as S-Boxes you get SL1
+ * By passing is1, is2, sb1, sb2 as S-Boxes you get SL2
+ */
+static inline void aria_sl( uint32_t *a, uint32_t *b,
+ uint32_t *c, uint32_t *d,
+ const uint8_t sa[256], const uint8_t sb[256],
+ const uint8_t sc[256], const uint8_t sd[256] )
+{
+ *a = ( (uint32_t) sa[ *a & 0xFF] ) ^
+ (((uint32_t) sb[(*a >> 8) & 0xFF]) << 8) ^
+ (((uint32_t) sc[(*a >> 16) & 0xFF]) << 16) ^
+ (((uint32_t) sd[ *a >> 24 ]) << 24);
+ *b = ( (uint32_t) sa[ *b & 0xFF] ) ^
+ (((uint32_t) sb[(*b >> 8) & 0xFF]) << 8) ^
+ (((uint32_t) sc[(*b >> 16) & 0xFF]) << 16) ^
+ (((uint32_t) sd[ *b >> 24 ]) << 24);
+ *c = ( (uint32_t) sa[ *c & 0xFF] ) ^
+ (((uint32_t) sb[(*c >> 8) & 0xFF]) << 8) ^
+ (((uint32_t) sc[(*c >> 16) & 0xFF]) << 16) ^
+ (((uint32_t) sd[ *c >> 24 ]) << 24);
+ *d = ( (uint32_t) sa[ *d & 0xFF] ) ^
+ (((uint32_t) sb[(*d >> 8) & 0xFF]) << 8) ^
+ (((uint32_t) sc[(*d >> 16) & 0xFF]) << 16) ^
+ (((uint32_t) sd[ *d >> 24 ]) << 24);
+}
+
+/*
+ * S-Boxes
+ */
+static const uint8_t aria_sb1[256] =
+{
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
+ 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
+ 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
+ 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
+ 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
+ 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
+ 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
+ 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
+ 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
+ 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
+ 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
+ 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
+ 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
+ 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
+ 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
+ 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
+ 0xB0, 0x54, 0xBB, 0x16
+};
+
+static const uint8_t aria_sb2[256] =
+{
+ 0xE2, 0x4E, 0x54, 0xFC, 0x94, 0xC2, 0x4A, 0xCC, 0x62, 0x0D, 0x6A, 0x46,
+ 0x3C, 0x4D, 0x8B, 0xD1, 0x5E, 0xFA, 0x64, 0xCB, 0xB4, 0x97, 0xBE, 0x2B,
+ 0xBC, 0x77, 0x2E, 0x03, 0xD3, 0x19, 0x59, 0xC1, 0x1D, 0x06, 0x41, 0x6B,
+ 0x55, 0xF0, 0x99, 0x69, 0xEA, 0x9C, 0x18, 0xAE, 0x63, 0xDF, 0xE7, 0xBB,
+ 0x00, 0x73, 0x66, 0xFB, 0x96, 0x4C, 0x85, 0xE4, 0x3A, 0x09, 0x45, 0xAA,
+ 0x0F, 0xEE, 0x10, 0xEB, 0x2D, 0x7F, 0xF4, 0x29, 0xAC, 0xCF, 0xAD, 0x91,
+ 0x8D, 0x78, 0xC8, 0x95, 0xF9, 0x2F, 0xCE, 0xCD, 0x08, 0x7A, 0x88, 0x38,
+ 0x5C, 0x83, 0x2A, 0x28, 0x47, 0xDB, 0xB8, 0xC7, 0x93, 0xA4, 0x12, 0x53,
+ 0xFF, 0x87, 0x0E, 0x31, 0x36, 0x21, 0x58, 0x48, 0x01, 0x8E, 0x37, 0x74,
+ 0x32, 0xCA, 0xE9, 0xB1, 0xB7, 0xAB, 0x0C, 0xD7, 0xC4, 0x56, 0x42, 0x26,
+ 0x07, 0x98, 0x60, 0xD9, 0xB6, 0xB9, 0x11, 0x40, 0xEC, 0x20, 0x8C, 0xBD,
+ 0xA0, 0xC9, 0x84, 0x04, 0x49, 0x23, 0xF1, 0x4F, 0x50, 0x1F, 0x13, 0xDC,
+ 0xD8, 0xC0, 0x9E, 0x57, 0xE3, 0xC3, 0x7B, 0x65, 0x3B, 0x02, 0x8F, 0x3E,
+ 0xE8, 0x25, 0x92, 0xE5, 0x15, 0xDD, 0xFD, 0x17, 0xA9, 0xBF, 0xD4, 0x9A,
+ 0x7E, 0xC5, 0x39, 0x67, 0xFE, 0x76, 0x9D, 0x43, 0xA7, 0xE1, 0xD0, 0xF5,
+ 0x68, 0xF2, 0x1B, 0x34, 0x70, 0x05, 0xA3, 0x8A, 0xD5, 0x79, 0x86, 0xA8,
+ 0x30, 0xC6, 0x51, 0x4B, 0x1E, 0xA6, 0x27, 0xF6, 0x35, 0xD2, 0x6E, 0x24,
+ 0x16, 0x82, 0x5F, 0xDA, 0xE6, 0x75, 0xA2, 0xEF, 0x2C, 0xB2, 0x1C, 0x9F,
+ 0x5D, 0x6F, 0x80, 0x0A, 0x72, 0x44, 0x9B, 0x6C, 0x90, 0x0B, 0x5B, 0x33,
+ 0x7D, 0x5A, 0x52, 0xF3, 0x61, 0xA1, 0xF7, 0xB0, 0xD6, 0x3F, 0x7C, 0x6D,
+ 0xED, 0x14, 0xE0, 0xA5, 0x3D, 0x22, 0xB3, 0xF8, 0x89, 0xDE, 0x71, 0x1A,
+ 0xAF, 0xBA, 0xB5, 0x81
+};
+
+static const uint8_t aria_is1[256] =
+{
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E,
+ 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+ 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32,
+ 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49,
+ 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+ 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50,
+ 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05,
+ 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+ 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41,
+ 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8,
+ 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+ 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B,
+ 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59,
+ 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+ 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D,
+ 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63,
+ 0x55, 0x21, 0x0C, 0x7D
+};
+
+static const uint8_t aria_is2[256] =
+{
+ 0x30, 0x68, 0x99, 0x1B, 0x87, 0xB9, 0x21, 0x78, 0x50, 0x39, 0xDB, 0xE1,
+ 0x72, 0x09, 0x62, 0x3C, 0x3E, 0x7E, 0x5E, 0x8E, 0xF1, 0xA0, 0xCC, 0xA3,
+ 0x2A, 0x1D, 0xFB, 0xB6, 0xD6, 0x20, 0xC4, 0x8D, 0x81, 0x65, 0xF5, 0x89,
+ 0xCB, 0x9D, 0x77, 0xC6, 0x57, 0x43, 0x56, 0x17, 0xD4, 0x40, 0x1A, 0x4D,
+ 0xC0, 0x63, 0x6C, 0xE3, 0xB7, 0xC8, 0x64, 0x6A, 0x53, 0xAA, 0x38, 0x98,
+ 0x0C, 0xF4, 0x9B, 0xED, 0x7F, 0x22, 0x76, 0xAF, 0xDD, 0x3A, 0x0B, 0x58,
+ 0x67, 0x88, 0x06, 0xC3, 0x35, 0x0D, 0x01, 0x8B, 0x8C, 0xC2, 0xE6, 0x5F,
+ 0x02, 0x24, 0x75, 0x93, 0x66, 0x1E, 0xE5, 0xE2, 0x54, 0xD8, 0x10, 0xCE,
+ 0x7A, 0xE8, 0x08, 0x2C, 0x12, 0x97, 0x32, 0xAB, 0xB4, 0x27, 0x0A, 0x23,
+ 0xDF, 0xEF, 0xCA, 0xD9, 0xB8, 0xFA, 0xDC, 0x31, 0x6B, 0xD1, 0xAD, 0x19,
+ 0x49, 0xBD, 0x51, 0x96, 0xEE, 0xE4, 0xA8, 0x41, 0xDA, 0xFF, 0xCD, 0x55,
+ 0x86, 0x36, 0xBE, 0x61, 0x52, 0xF8, 0xBB, 0x0E, 0x82, 0x48, 0x69, 0x9A,
+ 0xE0, 0x47, 0x9E, 0x5C, 0x04, 0x4B, 0x34, 0x15, 0x79, 0x26, 0xA7, 0xDE,
+ 0x29, 0xAE, 0x92, 0xD7, 0x84, 0xE9, 0xD2, 0xBA, 0x5D, 0xF3, 0xC5, 0xB0,
+ 0xBF, 0xA4, 0x3B, 0x71, 0x44, 0x46, 0x2B, 0xFC, 0xEB, 0x6F, 0xD5, 0xF6,
+ 0x14, 0xFE, 0x7C, 0x70, 0x5A, 0x7D, 0xFD, 0x2F, 0x18, 0x83, 0x16, 0xA5,
+ 0x91, 0x1F, 0x05, 0x95, 0x74, 0xA9, 0xC1, 0x5B, 0x4A, 0x85, 0x6D, 0x13,
+ 0x07, 0x4F, 0x4E, 0x45, 0xB2, 0x0F, 0xC9, 0x1C, 0xA6, 0xBC, 0xEC, 0x73,
+ 0x90, 0x7B, 0xCF, 0x59, 0x8F, 0xA1, 0xF9, 0x2D, 0xF2, 0xB1, 0x00, 0x94,
+ 0x37, 0x9F, 0xD0, 0x2E, 0x9C, 0x6E, 0x28, 0x3F, 0x80, 0xF0, 0x3D, 0xD3,
+ 0x25, 0x8A, 0xB5, 0xE7, 0x42, 0xB3, 0xC7, 0xEA, 0xF7, 0x4C, 0x11, 0x33,
+ 0x03, 0xA2, 0xAC, 0x60
+};
+
+/*
+ * Helper for key schedule: r = FO( p, k ) ^ x
+ */
+static void aria_fo_xor( uint32_t r[4], const uint32_t p[4],
+ const uint32_t k[4], const uint32_t x[4] )
+{
+ uint32_t a, b, c, d;
+
+ a = p[0] ^ k[0];
+ b = p[1] ^ k[1];
+ c = p[2] ^ k[2];
+ d = p[3] ^ k[3];
+
+ aria_sl( &a, &b, &c, &d, aria_sb1, aria_sb2, aria_is1, aria_is2 );
+ aria_a( &a, &b, &c, &d );
+
+ r[0] = a ^ x[0];
+ r[1] = b ^ x[1];
+ r[2] = c ^ x[2];
+ r[3] = d ^ x[3];
+}
+
+/*
+ * Helper for key schedule: r = FE( p, k ) ^ x
+ */
+static void aria_fe_xor( uint32_t r[4], const uint32_t p[4],
+ const uint32_t k[4], const uint32_t x[4] )
+{
+ uint32_t a, b, c, d;
+
+ a = p[0] ^ k[0];
+ b = p[1] ^ k[1];
+ c = p[2] ^ k[2];
+ d = p[3] ^ k[3];
+
+ aria_sl( &a, &b, &c, &d, aria_is1, aria_is2, aria_sb1, aria_sb2 );
+ aria_a( &a, &b, &c, &d );
+
+ r[0] = a ^ x[0];
+ r[1] = b ^ x[1];
+ r[2] = c ^ x[2];
+ r[3] = d ^ x[3];
+}
+
+/*
+ * Big endian 128-bit rotation: r = a ^ (b <<< n), used only in key setup.
+ *
+ * We chose to store bytes into 32-bit words in little-endian format (see
+ * GET/PUT_UINT32_LE) so we need to reverse bytes here.
+ */
+static void aria_rot128( uint32_t r[4], const uint32_t a[4],
+ const uint32_t b[4], uint8_t n )
+{
+ uint8_t i, j;
+ uint32_t t, u;
+
+ const uint8_t n1 = n % 32; // bit offset
+ const uint8_t n2 = n1 ? 32 - n1 : 0; // reverse bit offset
+
+ j = ( n / 32 ) % 4; // initial word offset
+ t = ARIA_P3( b[j] ); // big endian
+ for( i = 0; i < 4; i++ )
+ {
+ j = ( j + 1 ) % 4; // get next word, big endian
+ u = ARIA_P3( b[j] );
+ t <<= n1; // rotate
+ t |= u >> n2;
+ t = ARIA_P3( t ); // back to little endian
+ r[i] = a[i] ^ t; // store
+ t = u; // move to next word
+ }
+}
+
+/*
+ * Set encryption key
+ */
+int mbedtls_aria_setkey_enc( mbedtls_aria_context *ctx,
+ const unsigned char *key, unsigned int keybits )
+{
+ /* round constant masks */
+ const uint32_t rc[3][4] =
+ {
+ { 0xB7C17C51, 0x940A2227, 0xE8AB13FE, 0xE06E9AFA },
+ { 0xCC4AB16D, 0x20C8219E, 0xD5B128FF, 0xB0E25DEF },
+ { 0x1D3792DB, 0x70E92621, 0x75972403, 0x0EC9E804 }
+ };
+
+ int i;
+ uint32_t w[4][4], *w2;
+
+ if( keybits != 128 && keybits != 192 && keybits != 256 )
+ return( MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH );
+
+ /* Copy key to W0 (and potential remainder to W1) */
+ GET_UINT32_LE( w[0][0], key, 0 );
+ GET_UINT32_LE( w[0][1], key, 4 );
+ GET_UINT32_LE( w[0][2], key, 8 );
+ GET_UINT32_LE( w[0][3], key, 12 );
+
+ memset( w[1], 0, 16 );
+ if( keybits >= 192 )
+ {
+ GET_UINT32_LE( w[1][0], key, 16 ); // 192 bit key
+ GET_UINT32_LE( w[1][1], key, 20 );
+ }
+ if( keybits == 256 )
+ {
+ GET_UINT32_LE( w[1][2], key, 24 ); // 256 bit key
+ GET_UINT32_LE( w[1][3], key, 28 );
+ }
+
+ i = ( keybits - 128 ) >> 6; // index: 0, 1, 2
+ ctx->nr = 12 + 2 * i; // no. rounds: 12, 14, 16
+
+ aria_fo_xor( w[1], w[0], rc[i], w[1] ); // W1 = FO(W0, CK1) ^ KR
+ i = i < 2 ? i + 1 : 0;
+ aria_fe_xor( w[2], w[1], rc[i], w[0] ); // W2 = FE(W1, CK2) ^ W0
+ i = i < 2 ? i + 1 : 0;
+ aria_fo_xor( w[3], w[2], rc[i], w[1] ); // W3 = FO(W2, CK3) ^ W1
+
+ for( i = 0; i < 4; i++ ) // create round keys
+ {
+ w2 = w[(i + 1) & 3];
+ aria_rot128( ctx->rk[i ], w[i], w2, 128 - 19 );
+ aria_rot128( ctx->rk[i + 4], w[i], w2, 128 - 31 );
+ aria_rot128( ctx->rk[i + 8], w[i], w2, 61 );
+ aria_rot128( ctx->rk[i + 12], w[i], w2, 31 );
+ }
+ aria_rot128( ctx->rk[16], w[0], w[1], 19 );
+
+ /* w holds enough info to reconstruct the round keys */
+ mbedtls_platform_zeroize( w, sizeof( w ) );
+
+ return( 0 );
+}
+
+/*
+ * Set decryption key
+ */
+int mbedtls_aria_setkey_dec( mbedtls_aria_context *ctx,
+ const unsigned char *key, unsigned int keybits )
+{
+ int i, j, k, ret;
+
+ ret = mbedtls_aria_setkey_enc( ctx, key, keybits );
+ if( ret != 0 )
+ return( ret );
+
+ /* flip the order of round keys */
+ for( i = 0, j = ctx->nr; i < j; i++, j-- )
+ {
+ for( k = 0; k < 4; k++ )
+ {
+ uint32_t t = ctx->rk[i][k];
+ ctx->rk[i][k] = ctx->rk[j][k];
+ ctx->rk[j][k] = t;
+ }
+ }
+
+ /* apply affine transform to middle keys */
+ for( i = 1; i < ctx->nr; i++ )
+ {
+ aria_a( &ctx->rk[i][0], &ctx->rk[i][1],
+ &ctx->rk[i][2], &ctx->rk[i][3] );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Encrypt a block
+ */
+int mbedtls_aria_crypt_ecb( mbedtls_aria_context *ctx,
+ const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE],
+ unsigned char output[MBEDTLS_ARIA_BLOCKSIZE] )
+{
+ int i;
+
+ uint32_t a, b, c, d;
+
+ GET_UINT32_LE( a, input, 0 );
+ GET_UINT32_LE( b, input, 4 );
+ GET_UINT32_LE( c, input, 8 );
+ GET_UINT32_LE( d, input, 12 );
+
+ i = 0;
+ while( 1 )
+ {
+ a ^= ctx->rk[i][0];
+ b ^= ctx->rk[i][1];
+ c ^= ctx->rk[i][2];
+ d ^= ctx->rk[i][3];
+ i++;
+
+ aria_sl( &a, &b, &c, &d, aria_sb1, aria_sb2, aria_is1, aria_is2 );
+ aria_a( &a, &b, &c, &d );
+
+ a ^= ctx->rk[i][0];
+ b ^= ctx->rk[i][1];
+ c ^= ctx->rk[i][2];
+ d ^= ctx->rk[i][3];
+ i++;
+
+ aria_sl( &a, &b, &c, &d, aria_is1, aria_is2, aria_sb1, aria_sb2 );
+ if( i >= ctx->nr )
+ break;
+ aria_a( &a, &b, &c, &d );
+ }
+
+ /* final key mixing */
+ a ^= ctx->rk[i][0];
+ b ^= ctx->rk[i][1];
+ c ^= ctx->rk[i][2];
+ d ^= ctx->rk[i][3];
+
+ PUT_UINT32_LE( a, output, 0 );
+ PUT_UINT32_LE( b, output, 4 );
+ PUT_UINT32_LE( c, output, 8 );
+ PUT_UINT32_LE( d, output, 12 );
+
+ return( 0 );
+}
+
+/* Initialize context */
+void mbedtls_aria_init( mbedtls_aria_context *ctx )
+{
+ memset( ctx, 0, sizeof( mbedtls_aria_context ) );
+}
+
+/* Clear context */
+void mbedtls_aria_free( mbedtls_aria_context *ctx )
+{
+ if( ctx == NULL )
+ return;
+
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_aria_context ) );
+}
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+/*
+ * ARIA-CBC buffer encryption/decryption
+ */
+int mbedtls_aria_crypt_cbc( mbedtls_aria_context *ctx,
+ int mode,
+ size_t length,
+ unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output )
+{
+ int i;
+ unsigned char temp[MBEDTLS_ARIA_BLOCKSIZE];
+
+ if( length % MBEDTLS_ARIA_BLOCKSIZE )
+ return( MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH );
+
+ if( mode == MBEDTLS_ARIA_DECRYPT )
+ {
+ while( length > 0 )
+ {
+ memcpy( temp, input, MBEDTLS_ARIA_BLOCKSIZE );
+ mbedtls_aria_crypt_ecb( ctx, input, output );
+
+ for( i = 0; i < MBEDTLS_ARIA_BLOCKSIZE; i++ )
+ output[i] = (unsigned char)( output[i] ^ iv[i] );
+
+ memcpy( iv, temp, MBEDTLS_ARIA_BLOCKSIZE );
+
+ input += MBEDTLS_ARIA_BLOCKSIZE;
+ output += MBEDTLS_ARIA_BLOCKSIZE;
+ length -= MBEDTLS_ARIA_BLOCKSIZE;
+ }
+ }
+ else
+ {
+ while( length > 0 )
+ {
+ for( i = 0; i < MBEDTLS_ARIA_BLOCKSIZE; i++ )
+ output[i] = (unsigned char)( input[i] ^ iv[i] );
+
+ mbedtls_aria_crypt_ecb( ctx, output, output );
+ memcpy( iv, output, MBEDTLS_ARIA_BLOCKSIZE );
+
+ input += MBEDTLS_ARIA_BLOCKSIZE;
+ output += MBEDTLS_ARIA_BLOCKSIZE;
+ length -= MBEDTLS_ARIA_BLOCKSIZE;
+ }
+ }
+
+ return( 0 );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+/*
+ * ARIA-CFB128 buffer encryption/decryption
+ */
+int mbedtls_aria_crypt_cfb128( mbedtls_aria_context *ctx,
+ int mode,
+ size_t length,
+ size_t *iv_off,
+ unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output )
+{
+ unsigned char c;
+ size_t n = *iv_off;
+
+ if( mode == MBEDTLS_ARIA_DECRYPT )
+ {
+ while( length-- )
+ {
+ if( n == 0 )
+ mbedtls_aria_crypt_ecb( ctx, iv, iv );
+
+ c = *input++;
+ *output++ = c ^ iv[n];
+ iv[n] = c;
+
+ n = ( n + 1 ) & 0x0F;
+ }
+ }
+ else
+ {
+ while( length-- )
+ {
+ if( n == 0 )
+ mbedtls_aria_crypt_ecb( ctx, iv, iv );
+
+ iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ );
+
+ n = ( n + 1 ) & 0x0F;
+ }
+ }
+
+ *iv_off = n;
+
+ return( 0 );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+/*
+ * ARIA-CTR buffer encryption/decryption
+ */
+int mbedtls_aria_crypt_ctr( mbedtls_aria_context *ctx,
+ size_t length,
+ size_t *nc_off,
+ unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE],
+ unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE],
+ const unsigned char *input,
+ unsigned char *output )
+{
+ int c, i;
+ size_t n = *nc_off;
+
+ while( length-- )
+ {
+ if( n == 0 ) {
+ mbedtls_aria_crypt_ecb( ctx, nonce_counter,
+ stream_block );
+
+ for( i = MBEDTLS_ARIA_BLOCKSIZE; i > 0; i-- )
+ if( ++nonce_counter[i - 1] != 0 )
+ break;
+ }
+ c = *input++;
+ *output++ = (unsigned char)( c ^ stream_block[n] );
+
+ n = ( n + 1 ) & 0x0F;
+ }
+
+ *nc_off = n;
+
+ return( 0 );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CTR */
+#endif /* !MBEDTLS_ARIA_ALT */
+
+#if defined(MBEDTLS_SELF_TEST)
+
+/*
+ * Basic ARIA ECB test vectors from RFC 5794
+ */
+static const uint8_t aria_test1_ecb_key[32] = // test key
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 128 bit
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 192 bit
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 256 bit
+};
+
+static const uint8_t aria_test1_ecb_pt[MBEDTLS_ARIA_BLOCKSIZE] = // plaintext
+{
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // same for all
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF // key sizes
+};
+
+static const uint8_t aria_test1_ecb_ct[3][MBEDTLS_ARIA_BLOCKSIZE] = // ciphertext
+{
+ { 0xD7, 0x18, 0xFB, 0xD6, 0xAB, 0x64, 0x4C, 0x73, // 128 bit
+ 0x9D, 0xA9, 0x5F, 0x3B, 0xE6, 0x45, 0x17, 0x78 },
+ { 0x26, 0x44, 0x9C, 0x18, 0x05, 0xDB, 0xE7, 0xAA, // 192 bit
+ 0x25, 0xA4, 0x68, 0xCE, 0x26, 0x3A, 0x9E, 0x79 },
+ { 0xF9, 0x2B, 0xD7, 0xC7, 0x9F, 0xB7, 0x2E, 0x2F, // 256 bit
+ 0x2B, 0x8F, 0x80, 0xC1, 0x97, 0x2D, 0x24, 0xFC }
+};
+
+/*
+ * Mode tests from "Test Vectors for ARIA" Version 1.0
+ * http://210.104.33.10/ARIA/doc/ARIA-testvector-e.pdf
+ */
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB) || \
+ defined(MBEDTLS_CIPHER_MODE_CTR))
+static const uint8_t aria_test2_key[32] =
+{
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 128 bit
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 192 bit
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff // 256 bit
+};
+
+static const uint8_t aria_test2_pt[48] =
+{
+ 0x11, 0x11, 0x11, 0x11, 0xaa, 0xaa, 0xaa, 0xaa, // same for all
+ 0x11, 0x11, 0x11, 0x11, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0x11, 0x11, 0x11, 0x11, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x11, 0x11, 0x11, 0x11, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0x22, 0x22, 0x22, 0x22, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0x22, 0x22, 0x22, 0x22, 0xbb, 0xbb, 0xbb, 0xbb,
+};
+#endif
+
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB))
+static const uint8_t aria_test2_iv[MBEDTLS_ARIA_BLOCKSIZE] =
+{
+ 0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78, // same for CBC, CFB
+ 0x87, 0x96, 0xa5, 0xb4, 0xc3, 0xd2, 0xe1, 0xf0 // CTR has zero IV
+};
+#endif
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+static const uint8_t aria_test2_cbc_ct[3][48] = // CBC ciphertext
+{
+ { 0x49, 0xd6, 0x18, 0x60, 0xb1, 0x49, 0x09, 0x10, // 128-bit key
+ 0x9c, 0xef, 0x0d, 0x22, 0xa9, 0x26, 0x81, 0x34,
+ 0xfa, 0xdf, 0x9f, 0xb2, 0x31, 0x51, 0xe9, 0x64,
+ 0x5f, 0xba, 0x75, 0x01, 0x8b, 0xdb, 0x15, 0x38,
+ 0xb5, 0x33, 0x34, 0x63, 0x4b, 0xbf, 0x7d, 0x4c,
+ 0xd4, 0xb5, 0x37, 0x70, 0x33, 0x06, 0x0c, 0x15 },
+ { 0xaf, 0xe6, 0xcf, 0x23, 0x97, 0x4b, 0x53, 0x3c, // 192-bit key
+ 0x67, 0x2a, 0x82, 0x62, 0x64, 0xea, 0x78, 0x5f,
+ 0x4e, 0x4f, 0x7f, 0x78, 0x0d, 0xc7, 0xf3, 0xf1,
+ 0xe0, 0x96, 0x2b, 0x80, 0x90, 0x23, 0x86, 0xd5,
+ 0x14, 0xe9, 0xc3, 0xe7, 0x72, 0x59, 0xde, 0x92,
+ 0xdd, 0x11, 0x02, 0xff, 0xab, 0x08, 0x6c, 0x1e },
+ { 0x52, 0x3a, 0x8a, 0x80, 0x6a, 0xe6, 0x21, 0xf1, // 256-bit key
+ 0x55, 0xfd, 0xd2, 0x8d, 0xbc, 0x34, 0xe1, 0xab,
+ 0x7b, 0x9b, 0x42, 0x43, 0x2a, 0xd8, 0xb2, 0xef,
+ 0xb9, 0x6e, 0x23, 0xb1, 0x3f, 0x0a, 0x6e, 0x52,
+ 0xf3, 0x61, 0x85, 0xd5, 0x0a, 0xd0, 0x02, 0xc5,
+ 0xf6, 0x01, 0xbe, 0xe5, 0x49, 0x3f, 0x11, 0x8b }
+};
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+static const uint8_t aria_test2_cfb_ct[3][48] = // CFB ciphertext
+{
+ { 0x37, 0x20, 0xe5, 0x3b, 0xa7, 0xd6, 0x15, 0x38, // 128-bit key
+ 0x34, 0x06, 0xb0, 0x9f, 0x0a, 0x05, 0xa2, 0x00,
+ 0xc0, 0x7c, 0x21, 0xe6, 0x37, 0x0f, 0x41, 0x3a,
+ 0x5d, 0x13, 0x25, 0x00, 0xa6, 0x82, 0x85, 0x01,
+ 0x7c, 0x61, 0xb4, 0x34, 0xc7, 0xb7, 0xca, 0x96,
+ 0x85, 0xa5, 0x10, 0x71, 0x86, 0x1e, 0x4d, 0x4b },
+ { 0x41, 0x71, 0xf7, 0x19, 0x2b, 0xf4, 0x49, 0x54, // 192-bit key
+ 0x94, 0xd2, 0x73, 0x61, 0x29, 0x64, 0x0f, 0x5c,
+ 0x4d, 0x87, 0xa9, 0xa2, 0x13, 0x66, 0x4c, 0x94,
+ 0x48, 0x47, 0x7c, 0x6e, 0xcc, 0x20, 0x13, 0x59,
+ 0x8d, 0x97, 0x66, 0x95, 0x2d, 0xd8, 0xc3, 0x86,
+ 0x8f, 0x17, 0xe3, 0x6e, 0xf6, 0x6f, 0xd8, 0x4b },
+ { 0x26, 0x83, 0x47, 0x05, 0xb0, 0xf2, 0xc0, 0xe2, // 256-bit key
+ 0x58, 0x8d, 0x4a, 0x7f, 0x09, 0x00, 0x96, 0x35,
+ 0xf2, 0x8b, 0xb9, 0x3d, 0x8c, 0x31, 0xf8, 0x70,
+ 0xec, 0x1e, 0x0b, 0xdb, 0x08, 0x2b, 0x66, 0xfa,
+ 0x40, 0x2d, 0xd9, 0xc2, 0x02, 0xbe, 0x30, 0x0c,
+ 0x45, 0x17, 0xd1, 0x96, 0xb1, 0x4d, 0x4c, 0xe1 }
+};
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+static const uint8_t aria_test2_ctr_ct[3][48] = // CTR ciphertext
+{
+ { 0xac, 0x5d, 0x7d, 0xe8, 0x05, 0xa0, 0xbf, 0x1c, // 128-bit key
+ 0x57, 0xc8, 0x54, 0x50, 0x1a, 0xf6, 0x0f, 0xa1,
+ 0x14, 0x97, 0xe2, 0xa3, 0x45, 0x19, 0xde, 0xa1,
+ 0x56, 0x9e, 0x91, 0xe5, 0xb5, 0xcc, 0xae, 0x2f,
+ 0xf3, 0xbf, 0xa1, 0xbf, 0x97, 0x5f, 0x45, 0x71,
+ 0xf4, 0x8b, 0xe1, 0x91, 0x61, 0x35, 0x46, 0xc3 },
+ { 0x08, 0x62, 0x5c, 0xa8, 0xfe, 0x56, 0x9c, 0x19, // 192-bit key
+ 0xba, 0x7a, 0xf3, 0x76, 0x0a, 0x6e, 0xd1, 0xce,
+ 0xf4, 0xd1, 0x99, 0x26, 0x3e, 0x99, 0x9d, 0xde,
+ 0x14, 0x08, 0x2d, 0xbb, 0xa7, 0x56, 0x0b, 0x79,
+ 0xa4, 0xc6, 0xb4, 0x56, 0xb8, 0x70, 0x7d, 0xce,
+ 0x75, 0x1f, 0x98, 0x54, 0xf1, 0x88, 0x93, 0xdf },
+ { 0x30, 0x02, 0x6c, 0x32, 0x96, 0x66, 0x14, 0x17, // 256-bit key
+ 0x21, 0x17, 0x8b, 0x99, 0xc0, 0xa1, 0xf1, 0xb2,
+ 0xf0, 0x69, 0x40, 0x25, 0x3f, 0x7b, 0x30, 0x89,
+ 0xe2, 0xa3, 0x0e, 0xa8, 0x6a, 0xa3, 0xc8, 0x8f,
+ 0x59, 0x40, 0xf0, 0x5a, 0xd7, 0xee, 0x41, 0xd7,
+ 0x13, 0x47, 0xbb, 0x72, 0x61, 0xe3, 0x48, 0xf1 }
+};
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#define ARIA_SELF_TEST_IF_FAIL \
+ { \
+ if( verbose ) \
+ printf( "failed\n" ); \
+ return( 1 ); \
+ } else { \
+ if( verbose ) \
+ printf( "passed\n" ); \
+ }
+
+/*
+ * Checkup routine
+ */
+int mbedtls_aria_self_test( int verbose )
+{
+ int i;
+ uint8_t blk[MBEDTLS_ARIA_BLOCKSIZE];
+ mbedtls_aria_context ctx;
+
+#if (defined(MBEDTLS_CIPHER_MODE_CFB) || defined(MBEDTLS_CIPHER_MODE_CTR))
+ size_t j;
+#endif
+
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) || \
+ defined(MBEDTLS_CIPHER_MODE_CFB) || \
+ defined(MBEDTLS_CIPHER_MODE_CTR))
+ uint8_t buf[48], iv[MBEDTLS_ARIA_BLOCKSIZE];
+#endif
+
+ /*
+ * Test set 1
+ */
+ for( i = 0; i < 3; i++ )
+ {
+ /* test ECB encryption */
+ if( verbose )
+ printf( " ARIA-ECB-%d (enc): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test1_ecb_key, 128 + 64 * i );
+ mbedtls_aria_crypt_ecb( &ctx, aria_test1_ecb_pt, blk );
+ if( memcmp( blk, aria_test1_ecb_ct[i], MBEDTLS_ARIA_BLOCKSIZE ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+
+ /* test ECB decryption */
+ if( verbose )
+ printf( " ARIA-ECB-%d (dec): ", 128 + 64 * i );
+ mbedtls_aria_setkey_dec( &ctx, aria_test1_ecb_key, 128 + 64 * i );
+ mbedtls_aria_crypt_ecb( &ctx, aria_test1_ecb_ct[i], blk );
+ if( memcmp( blk, aria_test1_ecb_pt, MBEDTLS_ARIA_BLOCKSIZE ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+ }
+ if( verbose )
+ printf( "\n" );
+
+ /*
+ * Test set 2
+ */
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+ for( i = 0; i < 3; i++ )
+ {
+ /* Test CBC encryption */
+ if( verbose )
+ printf( " ARIA-CBC-%d (enc): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test2_key, 128 + 64 * i );
+ memcpy( iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE );
+ memset( buf, 0x55, sizeof( buf ) );
+ mbedtls_aria_crypt_cbc( &ctx, MBEDTLS_ARIA_ENCRYPT, 48, iv,
+ aria_test2_pt, buf );
+ if( memcmp( buf, aria_test2_cbc_ct[i], 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+
+ /* Test CBC decryption */
+ if( verbose )
+ printf( " ARIA-CBC-%d (dec): ", 128 + 64 * i );
+ mbedtls_aria_setkey_dec( &ctx, aria_test2_key, 128 + 64 * i );
+ memcpy( iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE );
+ memset( buf, 0xAA, sizeof( buf ) );
+ mbedtls_aria_crypt_cbc( &ctx, MBEDTLS_ARIA_DECRYPT, 48, iv,
+ aria_test2_cbc_ct[i], buf );
+ if( memcmp( buf, aria_test2_pt, 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+ }
+ if( verbose )
+ printf( "\n" );
+
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+ for( i = 0; i < 3; i++ )
+ {
+ /* Test CFB encryption */
+ if( verbose )
+ printf( " ARIA-CFB-%d (enc): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test2_key, 128 + 64 * i );
+ memcpy( iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE );
+ memset( buf, 0x55, sizeof( buf ) );
+ j = 0;
+ mbedtls_aria_crypt_cfb128( &ctx, MBEDTLS_ARIA_ENCRYPT, 48, &j, iv,
+ aria_test2_pt, buf );
+ if( memcmp( buf, aria_test2_cfb_ct[i], 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+
+ /* Test CFB decryption */
+ if( verbose )
+ printf( " ARIA-CFB-%d (dec): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test2_key, 128 + 64 * i );
+ memcpy( iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE );
+ memset( buf, 0xAA, sizeof( buf ) );
+ j = 0;
+ mbedtls_aria_crypt_cfb128( &ctx, MBEDTLS_ARIA_DECRYPT, 48, &j,
+ iv, aria_test2_cfb_ct[i], buf );
+ if( memcmp( buf, aria_test2_pt, 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+ }
+ if( verbose )
+ printf( "\n" );
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+ for( i = 0; i < 3; i++ )
+ {
+ /* Test CTR encryption */
+ if( verbose )
+ printf( " ARIA-CTR-%d (enc): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test2_key, 128 + 64 * i );
+ memset( iv, 0, MBEDTLS_ARIA_BLOCKSIZE ); // IV = 0
+ memset( buf, 0x55, sizeof( buf ) );
+ j = 0;
+ mbedtls_aria_crypt_ctr( &ctx, 48, &j, iv, blk,
+ aria_test2_pt, buf );
+ if( memcmp( buf, aria_test2_ctr_ct[i], 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+
+ /* Test CTR decryption */
+ if( verbose )
+ printf( " ARIA-CTR-%d (dec): ", 128 + 64 * i );
+ mbedtls_aria_setkey_enc( &ctx, aria_test2_key, 128 + 64 * i );
+ memset( iv, 0, MBEDTLS_ARIA_BLOCKSIZE ); // IV = 0
+ memset( buf, 0xAA, sizeof( buf ) );
+ j = 0;
+ mbedtls_aria_crypt_ctr( &ctx, 48, &j, iv, blk,
+ aria_test2_ctr_ct[i], buf );
+ if( memcmp( buf, aria_test2_pt, 48 ) != 0 )
+ ARIA_SELF_TEST_IF_FAIL;
+ }
+ if( verbose )
+ printf( "\n" );
+#endif /* MBEDTLS_CIPHER_MODE_CTR */
+
+ return( 0 );
+}
+
+#endif /* MBEDTLS_SELF_TEST */
+
+#endif /* MBEDTLS_ARIA_C */
diff --git a/thirdparty/mbedtls/library/asn1parse.c b/thirdparty/mbedtls/library/asn1parse.c
index 4dd65c03c0..171c340b8c 100644
--- a/thirdparty/mbedtls/library/asn1parse.c
+++ b/thirdparty/mbedtls/library/asn1parse.c
@@ -28,6 +28,7 @@
#if defined(MBEDTLS_ASN1_PARSE_C)
#include "mbedtls/asn1.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -43,11 +44,6 @@
#define mbedtls_free free
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* ASN.1 DER decoding routines
*/
@@ -313,7 +309,7 @@ int mbedtls_asn1_get_alg( unsigned char **p,
if( *p == end )
{
- mbedtls_zeroize( params, sizeof(mbedtls_asn1_buf) );
+ mbedtls_platform_zeroize( params, sizeof(mbedtls_asn1_buf) );
return( 0 );
}
@@ -358,7 +354,7 @@ void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *cur )
mbedtls_free( cur->oid.p );
mbedtls_free( cur->val.p );
- mbedtls_zeroize( cur, sizeof( mbedtls_asn1_named_data ) );
+ mbedtls_platform_zeroize( cur, sizeof( mbedtls_asn1_named_data ) );
}
void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head )
diff --git a/thirdparty/mbedtls/library/asn1write.c b/thirdparty/mbedtls/library/asn1write.c
index 69b61b205f..c01c836550 100644
--- a/thirdparty/mbedtls/library/asn1write.c
+++ b/thirdparty/mbedtls/library/asn1write.c
@@ -232,10 +232,6 @@ int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val )
int ret;
size_t len = 0;
- // TODO negative values and values larger than 128
- // DER format assumes 2s complement for numbers, so the leftmost bit
- // should be 0 for positive numbers and 1 for negative numbers.
- //
if( *p - start < 1 )
return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c
index 9f13da4421..423e375fd1 100644
--- a/thirdparty/mbedtls/library/bignum.c
+++ b/thirdparty/mbedtls/library/bignum.c
@@ -45,6 +45,7 @@
#include "mbedtls/bignum.h"
#include "mbedtls/bn_mul.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -58,16 +59,6 @@
#define mbedtls_free free
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_mpi_zeroize( mbedtls_mpi_uint *v, size_t n ) {
- volatile mbedtls_mpi_uint *p = v; while( n-- ) *p++ = 0;
-}
-
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */
#define biL (ciL << 3) /* bits in limb */
#define biH (ciL << 2) /* half limb size */
@@ -81,6 +72,12 @@ static void mbedtls_zeroize( void *v, size_t n ) {
#define BITS_TO_LIMBS(i) ( (i) / biL + ( (i) % biL != 0 ) )
#define CHARS_TO_LIMBS(i) ( (i) / ciL + ( (i) % ciL != 0 ) )
+/* Implementation that should never be optimized out by the compiler */
+static void mbedtls_mpi_zeroize( mbedtls_mpi_uint *v, size_t n )
+{
+ mbedtls_platform_zeroize( v, ciL * n );
+}
+
/*
* Initialize one MPI
*/
@@ -184,7 +181,7 @@ int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs )
*/
int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y )
{
- int ret;
+ int ret = 0;
size_t i;
if( X == Y )
@@ -203,9 +200,15 @@ int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y )
X->s = Y->s;
- MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i ) );
+ if( X->n < i )
+ {
+ MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i ) );
+ }
+ else
+ {
+ memset( X->p + i, 0, ( X->n - i ) * ciL );
+ }
- memset( X->p, 0, X->n * ciL );
memcpy( X->p, Y->p, i * ciL );
cleanup:
@@ -963,7 +966,7 @@ static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d )
while( c != 0 )
{
z = ( *d < c ); *d -= c;
- c = z; i++; d++;
+ c = z; d++;
}
}
@@ -1201,8 +1204,8 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + j ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
- for( i++; j > 0; j-- )
- mpi_mul_hlp( i - 1, A->p, X->p + j - 1, B->p[j - 1] );
+ for( ; j > 0; j-- )
+ mpi_mul_hlp( i, A->p, X->p + j - 1, B->p[j - 1] );
X->s = A->s * B->s;
@@ -1891,7 +1894,7 @@ int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( X, buf, size ) );
cleanup:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
return( ret );
}
@@ -2188,12 +2191,23 @@ int mbedtls_mpi_is_prime( const mbedtls_mpi *X,
/*
* Prime number generation
+ *
+ * If dh_flag is 0 and nbits is at least 1024, then the procedure
+ * follows the RSA probably-prime generation method of FIPS 186-4.
+ * NB. FIPS 186-4 only allows the specific bit lengths of 1024 and 1536.
*/
int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
- int ret;
+#ifdef MBEDTLS_HAVE_INT64
+// ceil(2^63.5)
+#define CEIL_MAXUINT_DIV_SQRT2 0xb504f333f9de6485ULL
+#else
+// ceil(2^31.5)
+#define CEIL_MAXUINT_DIV_SQRT2 0xb504f334U
+#endif
+ int ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
size_t k, n;
mbedtls_mpi_uint r;
mbedtls_mpi Y;
@@ -2205,69 +2219,66 @@ int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag,
n = BITS_TO_LIMBS( nbits );
- MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( X, n * ciL, f_rng, p_rng ) );
-
- k = mbedtls_mpi_bitlen( X );
- if( k > nbits ) MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, k - nbits + 1 ) );
-
- mbedtls_mpi_set_bit( X, nbits-1, 1 );
+ while( 1 )
+ {
+ MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( X, n * ciL, f_rng, p_rng ) );
+ /* make sure generated number is at least (nbits-1)+0.5 bits (FIPS 186-4 §B.3.3 steps 4.4, 5.5) */
+ if( X->p[n-1] < CEIL_MAXUINT_DIV_SQRT2 ) continue;
- X->p[0] |= 1;
+ k = n * biL;
+ if( k > nbits ) MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, k - nbits ) );
+ X->p[0] |= 1;
- if( dh_flag == 0 )
- {
- while( ( ret = mbedtls_mpi_is_prime( X, f_rng, p_rng ) ) != 0 )
+ if( dh_flag == 0 )
{
+ ret = mbedtls_mpi_is_prime( X, f_rng, p_rng );
+
if( ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE )
goto cleanup;
-
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 2 ) );
}
- }
- else
- {
- /*
- * An necessary condition for Y and X = 2Y + 1 to be prime
- * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3).
- * Make sure it is satisfied, while keeping X = 3 mod 4
- */
+ else
+ {
+ /*
+ * An necessary condition for Y and X = 2Y + 1 to be prime
+ * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3).
+ * Make sure it is satisfied, while keeping X = 3 mod 4
+ */
- X->p[0] |= 2;
+ X->p[0] |= 2;
- MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, 3 ) );
- if( r == 0 )
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 8 ) );
- else if( r == 1 )
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 4 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, 3 ) );
+ if( r == 0 )
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 8 ) );
+ else if( r == 1 )
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 4 ) );
- /* Set Y = (X-1) / 2, which is X / 2 because X is odd */
- MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Y, X ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Y, 1 ) );
+ /* Set Y = (X-1) / 2, which is X / 2 because X is odd */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Y, X ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Y, 1 ) );
- while( 1 )
- {
- /*
- * First, check small factors for X and Y
- * before doing Miller-Rabin on any of them
- */
- if( ( ret = mpi_check_small_factors( X ) ) == 0 &&
- ( ret = mpi_check_small_factors( &Y ) ) == 0 &&
- ( ret = mpi_miller_rabin( X, f_rng, p_rng ) ) == 0 &&
- ( ret = mpi_miller_rabin( &Y, f_rng, p_rng ) ) == 0 )
+ while( 1 )
{
- break;
+ /*
+ * First, check small factors for X and Y
+ * before doing Miller-Rabin on any of them
+ */
+ if( ( ret = mpi_check_small_factors( X ) ) == 0 &&
+ ( ret = mpi_check_small_factors( &Y ) ) == 0 &&
+ ( ret = mpi_miller_rabin( X, f_rng, p_rng ) ) == 0 &&
+ ( ret = mpi_miller_rabin( &Y, f_rng, p_rng ) ) == 0 )
+ goto cleanup;
+
+ if( ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE )
+ goto cleanup;
+
+ /*
+ * Next candidates. We want to preserve Y = (X-1) / 2 and
+ * Y = 1 mod 2 and Y = 2 mod 3 (eq X = 3 mod 4 and X = 2 mod 3)
+ * so up Y by 6 and X by 12.
+ */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 12 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &Y, &Y, 6 ) );
}
-
- if( ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE )
- goto cleanup;
-
- /*
- * Next candidates. We want to preserve Y = (X-1) / 2 and
- * Y = 1 mod 2 and Y = 2 mod 3 (eq X = 3 mod 4 and X = 2 mod 3)
- * so up Y by 6 and X by 12.
- */
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 12 ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &Y, &Y, 6 ) );
}
}
diff --git a/thirdparty/mbedtls/library/blowfish.c b/thirdparty/mbedtls/library/blowfish.c
index 9003f0dfeb..5b6bb9885f 100644
--- a/thirdparty/mbedtls/library/blowfish.c
+++ b/thirdparty/mbedtls/library/blowfish.c
@@ -34,16 +34,12 @@
#if defined(MBEDTLS_BLOWFISH_C)
#include "mbedtls/blowfish.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
#if !defined(MBEDTLS_BLOWFISH_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -165,7 +161,7 @@ void mbedtls_blowfish_free( mbedtls_blowfish_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_blowfish_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_blowfish_context ) );
}
/*
diff --git a/thirdparty/mbedtls/library/camellia.c b/thirdparty/mbedtls/library/camellia.c
index ac6f96a83a..41b7da0fae 100644
--- a/thirdparty/mbedtls/library/camellia.c
+++ b/thirdparty/mbedtls/library/camellia.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_CAMELLIA_C)
#include "mbedtls/camellia.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -48,11 +49,6 @@
#if !defined(MBEDTLS_CAMELLIA_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -333,7 +329,7 @@ void mbedtls_camellia_free( mbedtls_camellia_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_camellia_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_camellia_context ) );
}
/*
diff --git a/thirdparty/mbedtls/library/ccm.c b/thirdparty/mbedtls/library/ccm.c
index 9101e5f7c7..cf6520935e 100644
--- a/thirdparty/mbedtls/library/ccm.c
+++ b/thirdparty/mbedtls/library/ccm.c
@@ -37,6 +37,7 @@
#if defined(MBEDTLS_CCM_C)
#include "mbedtls/ccm.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -51,11 +52,6 @@
#if !defined(MBEDTLS_CCM_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
#define CCM_ENCRYPT 0
#define CCM_DECRYPT 1
@@ -102,7 +98,7 @@ int mbedtls_ccm_setkey( mbedtls_ccm_context *ctx,
void mbedtls_ccm_free( mbedtls_ccm_context *ctx )
{
mbedtls_cipher_free( &ctx->cipher_ctx );
- mbedtls_zeroize( ctx, sizeof( mbedtls_ccm_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ccm_context ) );
}
/*
@@ -343,7 +339,7 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
if( diff != 0 )
{
- mbedtls_zeroize( output, length );
+ mbedtls_platform_zeroize( output, length );
return( MBEDTLS_ERR_CCM_AUTH_FAILED );
}
diff --git a/thirdparty/mbedtls/library/cipher.c b/thirdparty/mbedtls/library/cipher.c
index ff0327380c..a5cd61cdf3 100644
--- a/thirdparty/mbedtls/library/cipher.c
+++ b/thirdparty/mbedtls/library/cipher.c
@@ -33,6 +33,7 @@
#include "mbedtls/cipher.h"
#include "mbedtls/cipher_internal.h"
+#include "mbedtls/platform_util.h"
#include <stdlib.h>
#include <string.h>
@@ -60,11 +61,6 @@
#define MBEDTLS_CIPHER_MODE_STREAM
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
static int supported_init = 0;
const int *mbedtls_cipher_list( void )
@@ -141,7 +137,8 @@ void mbedtls_cipher_free( mbedtls_cipher_context_t *ctx )
#if defined(MBEDTLS_CMAC_C)
if( ctx->cmac_ctx )
{
- mbedtls_zeroize( ctx->cmac_ctx, sizeof( mbedtls_cmac_context_t ) );
+ mbedtls_platform_zeroize( ctx->cmac_ctx,
+ sizeof( mbedtls_cmac_context_t ) );
mbedtls_free( ctx->cmac_ctx );
}
#endif
@@ -149,7 +146,7 @@ void mbedtls_cipher_free( mbedtls_cipher_context_t *ctx )
if( ctx->cipher_ctx )
ctx->cipher_info->base->ctx_free_func( ctx->cipher_ctx );
- mbedtls_zeroize( ctx, sizeof(mbedtls_cipher_context_t) );
+ mbedtls_platform_zeroize( ctx, sizeof(mbedtls_cipher_context_t) );
}
int mbedtls_cipher_setup( mbedtls_cipher_context_t *ctx, const mbedtls_cipher_info_t *cipher_info )
@@ -325,8 +322,10 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
/*
* If there is not enough data for a full block, cache it.
*/
- if( ( ctx->operation == MBEDTLS_DECRYPT &&
+ if( ( ctx->operation == MBEDTLS_DECRYPT && NULL != ctx->add_padding &&
ilen <= block_size - ctx->unprocessed_len ) ||
+ ( ctx->operation == MBEDTLS_DECRYPT && NULL == ctx->add_padding &&
+ ilen < block_size - ctx->unprocessed_len ) ||
( ctx->operation == MBEDTLS_ENCRYPT &&
ilen < block_size - ctx->unprocessed_len ) )
{
@@ -372,9 +371,17 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
return MBEDTLS_ERR_CIPHER_INVALID_CONTEXT;
}
+ /* Encryption: only cache partial blocks
+ * Decryption w/ padding: always keep at least one whole block
+ * Decryption w/o padding: only cache partial blocks
+ */
copy_len = ilen % block_size;
- if( copy_len == 0 && ctx->operation == MBEDTLS_DECRYPT )
+ if( copy_len == 0 &&
+ ctx->operation == MBEDTLS_DECRYPT &&
+ NULL != ctx->add_padding)
+ {
copy_len = block_size;
+ }
memcpy( ctx->unprocessed_data, &( input[ilen - copy_len] ),
copy_len );
diff --git a/thirdparty/mbedtls/library/cipher_wrap.c b/thirdparty/mbedtls/library/cipher_wrap.c
index dc76af8ff4..a9ef8195ca 100644
--- a/thirdparty/mbedtls/library/cipher_wrap.c
+++ b/thirdparty/mbedtls/library/cipher_wrap.c
@@ -45,6 +45,10 @@
#include "mbedtls/camellia.h"
#endif
+#if defined(MBEDTLS_ARIA_C)
+#include "mbedtls/aria.h"
+#endif
+
#if defined(MBEDTLS_DES_C)
#include "mbedtls/des.h"
#endif
@@ -822,6 +826,364 @@ static const mbedtls_cipher_info_t camellia_256_ccm_info = {
#endif /* MBEDTLS_CAMELLIA_C */
+#if defined(MBEDTLS_ARIA_C)
+
+static int aria_crypt_ecb_wrap( void *ctx, mbedtls_operation_t operation,
+ const unsigned char *input, unsigned char *output )
+{
+ (void) operation;
+ return mbedtls_aria_crypt_ecb( (mbedtls_aria_context *) ctx, input,
+ output );
+}
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+static int aria_crypt_cbc_wrap( void *ctx, mbedtls_operation_t operation,
+ size_t length, unsigned char *iv,
+ const unsigned char *input, unsigned char *output )
+{
+ return mbedtls_aria_crypt_cbc( (mbedtls_aria_context *) ctx, operation, length, iv,
+ input, output );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+static int aria_crypt_cfb128_wrap( void *ctx, mbedtls_operation_t operation,
+ size_t length, size_t *iv_off, unsigned char *iv,
+ const unsigned char *input, unsigned char *output )
+{
+ return mbedtls_aria_crypt_cfb128( (mbedtls_aria_context *) ctx, operation, length,
+ iv_off, iv, input, output );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+static int aria_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off,
+ unsigned char *nonce_counter, unsigned char *stream_block,
+ const unsigned char *input, unsigned char *output )
+{
+ return mbedtls_aria_crypt_ctr( (mbedtls_aria_context *) ctx, length, nc_off,
+ nonce_counter, stream_block, input, output );
+}
+#endif /* MBEDTLS_CIPHER_MODE_CTR */
+
+static int aria_setkey_dec_wrap( void *ctx, const unsigned char *key,
+ unsigned int key_bitlen )
+{
+ return mbedtls_aria_setkey_dec( (mbedtls_aria_context *) ctx, key, key_bitlen );
+}
+
+static int aria_setkey_enc_wrap( void *ctx, const unsigned char *key,
+ unsigned int key_bitlen )
+{
+ return mbedtls_aria_setkey_enc( (mbedtls_aria_context *) ctx, key, key_bitlen );
+}
+
+static void * aria_ctx_alloc( void )
+{
+ mbedtls_aria_context *ctx;
+ ctx = mbedtls_calloc( 1, sizeof( mbedtls_aria_context ) );
+
+ if( ctx == NULL )
+ return( NULL );
+
+ mbedtls_aria_init( ctx );
+
+ return( ctx );
+}
+
+static void aria_ctx_free( void *ctx )
+{
+ mbedtls_aria_free( (mbedtls_aria_context *) ctx );
+ mbedtls_free( ctx );
+}
+
+static const mbedtls_cipher_base_t aria_info = {
+ MBEDTLS_CIPHER_ID_ARIA,
+ aria_crypt_ecb_wrap,
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+ aria_crypt_cbc_wrap,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+ aria_crypt_cfb128_wrap,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+ aria_crypt_ctr_wrap,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_STREAM)
+ NULL,
+#endif
+ aria_setkey_enc_wrap,
+ aria_setkey_dec_wrap,
+ aria_ctx_alloc,
+ aria_ctx_free
+};
+
+static const mbedtls_cipher_info_t aria_128_ecb_info = {
+ MBEDTLS_CIPHER_ARIA_128_ECB,
+ MBEDTLS_MODE_ECB,
+ 128,
+ "ARIA-128-ECB",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_ecb_info = {
+ MBEDTLS_CIPHER_ARIA_192_ECB,
+ MBEDTLS_MODE_ECB,
+ 192,
+ "ARIA-192-ECB",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_ecb_info = {
+ MBEDTLS_CIPHER_ARIA_256_ECB,
+ MBEDTLS_MODE_ECB,
+ 256,
+ "ARIA-256-ECB",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+static const mbedtls_cipher_info_t aria_128_cbc_info = {
+ MBEDTLS_CIPHER_ARIA_128_CBC,
+ MBEDTLS_MODE_CBC,
+ 128,
+ "ARIA-128-CBC",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_cbc_info = {
+ MBEDTLS_CIPHER_ARIA_192_CBC,
+ MBEDTLS_MODE_CBC,
+ 192,
+ "ARIA-192-CBC",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_cbc_info = {
+ MBEDTLS_CIPHER_ARIA_256_CBC,
+ MBEDTLS_MODE_CBC,
+ 256,
+ "ARIA-256-CBC",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+#endif /* MBEDTLS_CIPHER_MODE_CBC */
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+static const mbedtls_cipher_info_t aria_128_cfb128_info = {
+ MBEDTLS_CIPHER_ARIA_128_CFB128,
+ MBEDTLS_MODE_CFB,
+ 128,
+ "ARIA-128-CFB128",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_cfb128_info = {
+ MBEDTLS_CIPHER_ARIA_192_CFB128,
+ MBEDTLS_MODE_CFB,
+ 192,
+ "ARIA-192-CFB128",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_cfb128_info = {
+ MBEDTLS_CIPHER_ARIA_256_CFB128,
+ MBEDTLS_MODE_CFB,
+ 256,
+ "ARIA-256-CFB128",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+#endif /* MBEDTLS_CIPHER_MODE_CFB */
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+static const mbedtls_cipher_info_t aria_128_ctr_info = {
+ MBEDTLS_CIPHER_ARIA_128_CTR,
+ MBEDTLS_MODE_CTR,
+ 128,
+ "ARIA-128-CTR",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_ctr_info = {
+ MBEDTLS_CIPHER_ARIA_192_CTR,
+ MBEDTLS_MODE_CTR,
+ 192,
+ "ARIA-192-CTR",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_ctr_info = {
+ MBEDTLS_CIPHER_ARIA_256_CTR,
+ MBEDTLS_MODE_CTR,
+ 256,
+ "ARIA-256-CTR",
+ 16,
+ 0,
+ 16,
+ &aria_info
+};
+#endif /* MBEDTLS_CIPHER_MODE_CTR */
+
+#if defined(MBEDTLS_GCM_C)
+static int gcm_aria_setkey_wrap( void *ctx, const unsigned char *key,
+ unsigned int key_bitlen )
+{
+ return mbedtls_gcm_setkey( (mbedtls_gcm_context *) ctx, MBEDTLS_CIPHER_ID_ARIA,
+ key, key_bitlen );
+}
+
+static const mbedtls_cipher_base_t gcm_aria_info = {
+ MBEDTLS_CIPHER_ID_ARIA,
+ NULL,
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_STREAM)
+ NULL,
+#endif
+ gcm_aria_setkey_wrap,
+ gcm_aria_setkey_wrap,
+ gcm_ctx_alloc,
+ gcm_ctx_free,
+};
+
+static const mbedtls_cipher_info_t aria_128_gcm_info = {
+ MBEDTLS_CIPHER_ARIA_128_GCM,
+ MBEDTLS_MODE_GCM,
+ 128,
+ "ARIA-128-GCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &gcm_aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_gcm_info = {
+ MBEDTLS_CIPHER_ARIA_192_GCM,
+ MBEDTLS_MODE_GCM,
+ 192,
+ "ARIA-192-GCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &gcm_aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_gcm_info = {
+ MBEDTLS_CIPHER_ARIA_256_GCM,
+ MBEDTLS_MODE_GCM,
+ 256,
+ "ARIA-256-GCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &gcm_aria_info
+};
+#endif /* MBEDTLS_GCM_C */
+
+#if defined(MBEDTLS_CCM_C)
+static int ccm_aria_setkey_wrap( void *ctx, const unsigned char *key,
+ unsigned int key_bitlen )
+{
+ return mbedtls_ccm_setkey( (mbedtls_ccm_context *) ctx, MBEDTLS_CIPHER_ID_ARIA,
+ key, key_bitlen );
+}
+
+static const mbedtls_cipher_base_t ccm_aria_info = {
+ MBEDTLS_CIPHER_ID_ARIA,
+ NULL,
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+ NULL,
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_STREAM)
+ NULL,
+#endif
+ ccm_aria_setkey_wrap,
+ ccm_aria_setkey_wrap,
+ ccm_ctx_alloc,
+ ccm_ctx_free,
+};
+
+static const mbedtls_cipher_info_t aria_128_ccm_info = {
+ MBEDTLS_CIPHER_ARIA_128_CCM,
+ MBEDTLS_MODE_CCM,
+ 128,
+ "ARIA-128-CCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &ccm_aria_info
+};
+
+static const mbedtls_cipher_info_t aria_192_ccm_info = {
+ MBEDTLS_CIPHER_ARIA_192_CCM,
+ MBEDTLS_MODE_CCM,
+ 192,
+ "ARIA-192-CCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &ccm_aria_info
+};
+
+static const mbedtls_cipher_info_t aria_256_ccm_info = {
+ MBEDTLS_CIPHER_ARIA_256_CCM,
+ MBEDTLS_MODE_CCM,
+ 256,
+ "ARIA-256-CCM",
+ 12,
+ MBEDTLS_CIPHER_VARIABLE_IV_LEN,
+ 16,
+ &ccm_aria_info
+};
+#endif /* MBEDTLS_CCM_C */
+
+#endif /* MBEDTLS_ARIA_C */
+
#if defined(MBEDTLS_DES_C)
static int des_crypt_ecb_wrap( void *ctx, mbedtls_operation_t operation,
@@ -1427,6 +1789,37 @@ const mbedtls_cipher_definition_t mbedtls_cipher_definitions[] =
#endif
#endif /* MBEDTLS_CAMELLIA_C */
+#if defined(MBEDTLS_ARIA_C)
+ { MBEDTLS_CIPHER_ARIA_128_ECB, &aria_128_ecb_info },
+ { MBEDTLS_CIPHER_ARIA_192_ECB, &aria_192_ecb_info },
+ { MBEDTLS_CIPHER_ARIA_256_ECB, &aria_256_ecb_info },
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+ { MBEDTLS_CIPHER_ARIA_128_CBC, &aria_128_cbc_info },
+ { MBEDTLS_CIPHER_ARIA_192_CBC, &aria_192_cbc_info },
+ { MBEDTLS_CIPHER_ARIA_256_CBC, &aria_256_cbc_info },
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+ { MBEDTLS_CIPHER_ARIA_128_CFB128, &aria_128_cfb128_info },
+ { MBEDTLS_CIPHER_ARIA_192_CFB128, &aria_192_cfb128_info },
+ { MBEDTLS_CIPHER_ARIA_256_CFB128, &aria_256_cfb128_info },
+#endif
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+ { MBEDTLS_CIPHER_ARIA_128_CTR, &aria_128_ctr_info },
+ { MBEDTLS_CIPHER_ARIA_192_CTR, &aria_192_ctr_info },
+ { MBEDTLS_CIPHER_ARIA_256_CTR, &aria_256_ctr_info },
+#endif
+#if defined(MBEDTLS_GCM_C)
+ { MBEDTLS_CIPHER_ARIA_128_GCM, &aria_128_gcm_info },
+ { MBEDTLS_CIPHER_ARIA_192_GCM, &aria_192_gcm_info },
+ { MBEDTLS_CIPHER_ARIA_256_GCM, &aria_256_gcm_info },
+#endif
+#if defined(MBEDTLS_CCM_C)
+ { MBEDTLS_CIPHER_ARIA_128_CCM, &aria_128_ccm_info },
+ { MBEDTLS_CIPHER_ARIA_192_CCM, &aria_192_ccm_info },
+ { MBEDTLS_CIPHER_ARIA_256_CCM, &aria_256_ccm_info },
+#endif
+#endif /* MBEDTLS_ARIA_C */
+
#if defined(MBEDTLS_DES_C)
{ MBEDTLS_CIPHER_DES_ECB, &des_ecb_info },
{ MBEDTLS_CIPHER_DES_EDE_ECB, &des_ede_ecb_info },
diff --git a/thirdparty/mbedtls/library/cmac.c b/thirdparty/mbedtls/library/cmac.c
index 9dbff90386..4d7a1f1693 100644
--- a/thirdparty/mbedtls/library/cmac.c
+++ b/thirdparty/mbedtls/library/cmac.c
@@ -49,6 +49,7 @@
#if defined(MBEDTLS_CMAC_C)
#include "mbedtls/cmac.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -67,11 +68,6 @@
#if !defined(MBEDTLS_CMAC_ALT) || defined(MBEDTLS_SELF_TEST)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* Multiplication by u in the Galois field of GF(2^n)
*
@@ -144,7 +140,7 @@ static int cmac_generate_subkeys( mbedtls_cipher_context_t *ctx,
unsigned char L[MBEDTLS_CIPHER_BLKSIZE_MAX];
size_t olen, block_size;
- mbedtls_zeroize( L, sizeof( L ) );
+ mbedtls_platform_zeroize( L, sizeof( L ) );
block_size = ctx->cipher_info->block_size;
@@ -162,7 +158,7 @@ static int cmac_generate_subkeys( mbedtls_cipher_context_t *ctx,
goto exit;
exit:
- mbedtls_zeroize( L, sizeof( L ) );
+ mbedtls_platform_zeroize( L, sizeof( L ) );
return( ret );
}
@@ -238,7 +234,7 @@ int mbedtls_cipher_cmac_starts( mbedtls_cipher_context_t *ctx,
ctx->cmac_ctx = cmac_ctx;
- mbedtls_zeroize( cmac_ctx->state, sizeof( cmac_ctx->state ) );
+ mbedtls_platform_zeroize( cmac_ctx->state, sizeof( cmac_ctx->state ) );
return 0;
}
@@ -330,8 +326,8 @@ int mbedtls_cipher_cmac_finish( mbedtls_cipher_context_t *ctx,
block_size = ctx->cipher_info->block_size;
state = cmac_ctx->state;
- mbedtls_zeroize( K1, sizeof( K1 ) );
- mbedtls_zeroize( K2, sizeof( K2 ) );
+ mbedtls_platform_zeroize( K1, sizeof( K1 ) );
+ mbedtls_platform_zeroize( K2, sizeof( K2 ) );
cmac_generate_subkeys( ctx, K1, K2 );
last_block = cmac_ctx->unprocessed_block;
@@ -361,14 +357,14 @@ int mbedtls_cipher_cmac_finish( mbedtls_cipher_context_t *ctx,
exit:
/* Wipe the generated keys on the stack, and any other transients to avoid
* side channel leakage */
- mbedtls_zeroize( K1, sizeof( K1 ) );
- mbedtls_zeroize( K2, sizeof( K2 ) );
+ mbedtls_platform_zeroize( K1, sizeof( K1 ) );
+ mbedtls_platform_zeroize( K2, sizeof( K2 ) );
cmac_ctx->unprocessed_len = 0;
- mbedtls_zeroize( cmac_ctx->unprocessed_block,
- sizeof( cmac_ctx->unprocessed_block ) );
+ mbedtls_platform_zeroize( cmac_ctx->unprocessed_block,
+ sizeof( cmac_ctx->unprocessed_block ) );
- mbedtls_zeroize( state, MBEDTLS_CIPHER_BLKSIZE_MAX );
+ mbedtls_platform_zeroize( state, MBEDTLS_CIPHER_BLKSIZE_MAX );
return( ret );
}
@@ -383,10 +379,10 @@ int mbedtls_cipher_cmac_reset( mbedtls_cipher_context_t *ctx )
/* Reset the internal state */
cmac_ctx->unprocessed_len = 0;
- mbedtls_zeroize( cmac_ctx->unprocessed_block,
- sizeof( cmac_ctx->unprocessed_block ) );
- mbedtls_zeroize( cmac_ctx->state,
- sizeof( cmac_ctx->state ) );
+ mbedtls_platform_zeroize( cmac_ctx->unprocessed_block,
+ sizeof( cmac_ctx->unprocessed_block ) );
+ mbedtls_platform_zeroize( cmac_ctx->state,
+ sizeof( cmac_ctx->state ) );
return( 0 );
}
@@ -466,7 +462,7 @@ int mbedtls_aes_cmac_prf_128( const unsigned char *key, size_t key_length,
output );
exit:
- mbedtls_zeroize( int_key, sizeof( int_key ) );
+ mbedtls_platform_zeroize( int_key, sizeof( int_key ) );
return( ret );
}
@@ -771,7 +767,7 @@ static int cmac_test_subkeys( int verbose,
int block_size,
int num_tests )
{
- int i, ret;
+ int i, ret = 0;
mbedtls_cipher_context_t ctx;
const mbedtls_cipher_info_t *cipher_info;
unsigned char K1[MBEDTLS_CIPHER_BLKSIZE_MAX];
@@ -853,7 +849,7 @@ static int cmac_test_wth_cipher( int verbose,
int num_tests )
{
const mbedtls_cipher_info_t *cipher_info;
- int i, ret;
+ int i, ret = 0;
unsigned char output[MBEDTLS_CIPHER_BLKSIZE_MAX];
cipher_info = mbedtls_cipher_info_from_type( cipher_type );
diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c
index ff532a0134..d0e5ba862d 100644
--- a/thirdparty/mbedtls/library/ctr_drbg.c
+++ b/thirdparty/mbedtls/library/ctr_drbg.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_CTR_DRBG_C)
#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -49,11 +50,6 @@
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* CTR_DRBG context initialization
*/
@@ -125,7 +121,7 @@ void mbedtls_ctr_drbg_free( mbedtls_ctr_drbg_context *ctx )
mbedtls_mutex_free( &ctx->mutex );
#endif
mbedtls_aes_free( &ctx->aes_ctx );
- mbedtls_zeroize( ctx, sizeof( mbedtls_ctr_drbg_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ctr_drbg_context ) );
}
void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx, int resistance )
@@ -245,16 +241,16 @@ exit:
/*
* tidy up the stack
*/
- mbedtls_zeroize( buf, sizeof( buf ) );
- mbedtls_zeroize( tmp, sizeof( tmp ) );
- mbedtls_zeroize( key, sizeof( key ) );
- mbedtls_zeroize( chain, sizeof( chain ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( tmp, sizeof( tmp ) );
+ mbedtls_platform_zeroize( key, sizeof( key ) );
+ mbedtls_platform_zeroize( chain, sizeof( chain ) );
if( 0 != ret )
{
/*
* wipe partial seed from memory
*/
- mbedtls_zeroize( output, MBEDTLS_CTR_DRBG_SEEDLEN );
+ mbedtls_platform_zeroize( output, MBEDTLS_CTR_DRBG_SEEDLEN );
}
return( ret );
@@ -493,7 +489,7 @@ int mbedtls_ctr_drbg_write_seed_file( mbedtls_ctr_drbg_context *ctx, const char
ret = 0;
exit:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
fclose( f );
return( ret );
@@ -526,7 +522,7 @@ int mbedtls_ctr_drbg_update_seed_file( mbedtls_ctr_drbg_context *ctx, const char
fclose( f );
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
if( ret != 0 )
return( ret );
diff --git a/thirdparty/mbedtls/library/des.c b/thirdparty/mbedtls/library/des.c
index 09f95cfc3b..ca9e071f32 100644
--- a/thirdparty/mbedtls/library/des.c
+++ b/thirdparty/mbedtls/library/des.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_DES_C)
#include "mbedtls/des.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -48,11 +49,6 @@
#if !defined(MBEDTLS_DES_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -316,7 +312,7 @@ void mbedtls_des_free( mbedtls_des_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_des_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_des_context ) );
}
void mbedtls_des3_init( mbedtls_des3_context *ctx )
@@ -329,7 +325,7 @@ void mbedtls_des3_free( mbedtls_des3_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_des3_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_des3_context ) );
}
static const unsigned char odd_parity_table[128] = { 1, 2, 4, 7, 8,
@@ -553,7 +549,7 @@ int mbedtls_des3_set2key_enc( mbedtls_des3_context *ctx,
uint32_t sk[96];
des3_set2key( ctx->sk, sk, key );
- mbedtls_zeroize( sk, sizeof( sk ) );
+ mbedtls_platform_zeroize( sk, sizeof( sk ) );
return( 0 );
}
@@ -567,7 +563,7 @@ int mbedtls_des3_set2key_dec( mbedtls_des3_context *ctx,
uint32_t sk[96];
des3_set2key( sk, ctx->sk, key );
- mbedtls_zeroize( sk, sizeof( sk ) );
+ mbedtls_platform_zeroize( sk, sizeof( sk ) );
return( 0 );
}
@@ -604,7 +600,7 @@ int mbedtls_des3_set3key_enc( mbedtls_des3_context *ctx,
uint32_t sk[96];
des3_set3key( ctx->sk, sk, key );
- mbedtls_zeroize( sk, sizeof( sk ) );
+ mbedtls_platform_zeroize( sk, sizeof( sk ) );
return( 0 );
}
@@ -618,7 +614,7 @@ int mbedtls_des3_set3key_dec( mbedtls_des3_context *ctx,
uint32_t sk[96];
des3_set3key( sk, ctx->sk, key );
- mbedtls_zeroize( sk, sizeof( sk ) );
+ mbedtls_platform_zeroize( sk, sizeof( sk ) );
return( 0 );
}
diff --git a/thirdparty/mbedtls/library/dhm.c b/thirdparty/mbedtls/library/dhm.c
index 28ac31003c..82cbb0ce88 100644
--- a/thirdparty/mbedtls/library/dhm.c
+++ b/thirdparty/mbedtls/library/dhm.c
@@ -36,6 +36,7 @@
#if defined(MBEDTLS_DHM_C)
#include "mbedtls/dhm.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -58,10 +59,6 @@
#endif
#if !defined(MBEDTLS_DHM_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
/*
* helper to validate the mbedtls_mpi size and import it
@@ -437,7 +434,7 @@ void mbedtls_dhm_free( mbedtls_dhm_context *ctx )
mbedtls_mpi_free( &ctx->GX ); mbedtls_mpi_free( &ctx->X );
mbedtls_mpi_free( &ctx->G ); mbedtls_mpi_free( &ctx->P );
- mbedtls_zeroize( ctx, sizeof( mbedtls_dhm_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_dhm_context ) );
}
#if defined(MBEDTLS_ASN1_PARSE_C)
@@ -575,7 +572,7 @@ static int load_file( const char *path, unsigned char **buf, size_t *n )
{
fclose( f );
- mbedtls_zeroize( *buf, *n + 1 );
+ mbedtls_platform_zeroize( *buf, *n + 1 );
mbedtls_free( *buf );
return( MBEDTLS_ERR_DHM_FILE_IO_ERROR );
@@ -605,7 +602,7 @@ int mbedtls_dhm_parse_dhmfile( mbedtls_dhm_context *dhm, const char *path )
ret = mbedtls_dhm_parse_dhm( dhm, buf, n );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c
index 826fefe5c6..17a88bdd29 100644
--- a/thirdparty/mbedtls/library/ecdsa.c
+++ b/thirdparty/mbedtls/library/ecdsa.c
@@ -400,6 +400,9 @@ int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx,
&ctx->Q, &r, &s ) ) != 0 )
goto cleanup;
+ /* At this point we know that the buffer starts with a valid signature.
+ * Return 0 if the buffer just contains the signature, and a specific
+ * error code if the valid signature is followed by more data. */
if( p != end )
ret = MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH;
diff --git a/thirdparty/mbedtls/library/ecjpake.c b/thirdparty/mbedtls/library/ecjpake.c
index e8f40862be..ec5a4007db 100644
--- a/thirdparty/mbedtls/library/ecjpake.c
+++ b/thirdparty/mbedtls/library/ecjpake.c
@@ -301,7 +301,7 @@ cleanup:
*/
static int ecjpake_zkp_write( const mbedtls_md_info_t *md_info,
const mbedtls_ecp_group *grp,
- const int pf,
+ const int pf,
const mbedtls_ecp_point *G,
const mbedtls_mpi *x,
const mbedtls_ecp_point *X,
diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c
index b41baef27a..41db3fbe5b 100644
--- a/thirdparty/mbedtls/library/ecp.c
+++ b/thirdparty/mbedtls/library/ecp.c
@@ -26,6 +26,7 @@
* GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone
* FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
* RFC 4492 for the related TLS structures and constants
+ * RFC 7748 for the Curve448 and Curve25519 curve definitions
*
* [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf
*
@@ -50,6 +51,7 @@
#include "mbedtls/ecp.h"
#include "mbedtls/threading.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -72,11 +74,6 @@
#define inline __inline
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
#if defined(MBEDTLS_SELF_TEST)
/*
* Counts of point addition and doubling, and field multiplications.
@@ -99,7 +96,8 @@ static unsigned long add_count, dbl_count, mul_count;
#define ECP_SHORTWEIERSTRASS
#endif
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || \
+ defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
#define ECP_MONTGOMERY
#endif
@@ -346,7 +344,7 @@ void mbedtls_ecp_group_free( mbedtls_ecp_group *grp )
mbedtls_free( grp->T );
}
- mbedtls_zeroize( grp, sizeof( mbedtls_ecp_group ) );
+ mbedtls_platform_zeroize( grp, sizeof( mbedtls_ecp_group ) );
}
/*
@@ -1852,6 +1850,8 @@ cleanup:
static int ecp_check_pubkey_mx( const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt )
{
/* [Curve25519 p. 5] Just check X is the correct number of bytes */
+ /* Allow any public value, if it's too big then we'll just reduce it mod p
+ * (RFC 7748 sec. 5 para. 3). */
if( mbedtls_mpi_size( &pt->X ) > ( grp->nbits + 7 ) / 8 )
return( MBEDTLS_ERR_ECP_INVALID_KEY );
@@ -1887,14 +1887,18 @@ int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, const mbedtls_mpi *
#if defined(ECP_MONTGOMERY)
if( ecp_get_type( grp ) == ECP_TYPE_MONTGOMERY )
{
- /* see [Curve25519] page 5 */
+ /* see RFC 7748 sec. 5 para. 5 */
if( mbedtls_mpi_get_bit( d, 0 ) != 0 ||
mbedtls_mpi_get_bit( d, 1 ) != 0 ||
- mbedtls_mpi_get_bit( d, 2 ) != 0 ||
mbedtls_mpi_bitlen( d ) - 1 != grp->nbits ) /* mbedtls_mpi_bitlen is one-based! */
return( MBEDTLS_ERR_ECP_INVALID_KEY );
else
- return( 0 );
+
+ /* see [Curve25519] page 5 */
+ if( grp->nbits == 254 && mbedtls_mpi_get_bit( d, 2 ) != 0 )
+ return( MBEDTLS_ERR_ECP_INVALID_KEY );
+
+ return( 0 );
}
#endif /* ECP_MONTGOMERY */
#if defined(ECP_SHORTWEIERSTRASS)
@@ -1941,10 +1945,14 @@ int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
else
MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, grp->nbits, 1 ) );
- /* Make sure the last three bits are unset */
+ /* Make sure the last two bits are unset for Curve448, three bits for
+ Curve25519 */
MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 0, 0 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 1, 0 ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) );
+ if( grp->nbits == 254 )
+ {
+ MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) );
+ }
}
else
#endif /* ECP_MONTGOMERY */
diff --git a/thirdparty/mbedtls/library/ecp_curves.c b/thirdparty/mbedtls/library/ecp_curves.c
index df5ac3eea5..68e2441ae8 100644
--- a/thirdparty/mbedtls/library/ecp_curves.c
+++ b/thirdparty/mbedtls/library/ecp_curves.c
@@ -627,6 +627,9 @@ static int ecp_mod_p521( mbedtls_mpi * );
#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
static int ecp_mod_p255( mbedtls_mpi * );
#endif
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+static int ecp_mod_p448( mbedtls_mpi * );
+#endif
#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
static int ecp_mod_p192k1( mbedtls_mpi * );
#endif
@@ -670,7 +673,12 @@ static int ecp_use_curve25519( mbedtls_ecp_group *grp )
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &grp->P, &grp->P, 19 ) );
grp->pbits = mbedtls_mpi_bitlen( &grp->P );
- /* Y intentionaly not set, since we use x/z coordinates.
+ /* N = 2^252 + 27742317777372353535851937790883648493 */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &grp->N, 16,
+ "14DEF9DEA2F79CD65812631A5CF5D3ED" ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( &grp->N, 252, 1 ) );
+
+ /* Y intentionally not set, since we use x/z coordinates.
* This is used as a marker to identify Montgomery curves! */
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.X, 9 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.Z, 1 ) );
@@ -687,6 +695,52 @@ cleanup:
}
#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+/*
+ * Specialized function for creating the Curve448 group
+ */
+static int ecp_use_curve448( mbedtls_ecp_group *grp )
+{
+ mbedtls_mpi Ns;
+ int ret;
+
+ mbedtls_mpi_init( &Ns );
+
+ /* Actually ( A + 2 ) / 4 */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &grp->A, 16, "98AA" ) );
+
+ /* P = 2^448 - 2^224 - 1 */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->P, 1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &grp->P, 224 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &grp->P, &grp->P, 1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &grp->P, 224 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &grp->P, &grp->P, 1 ) );
+ grp->pbits = mbedtls_mpi_bitlen( &grp->P );
+
+ /* Y intentionally not set, since we use x/z coordinates.
+ * This is used as a marker to identify Montgomery curves! */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.X, 5 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &grp->G.Z, 1 ) );
+ mbedtls_mpi_free( &grp->G.Y );
+
+ /* N = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885 */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( &grp->N, 446, 1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &Ns, 16,
+ "8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D" ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &grp->N, &grp->N, &Ns ) );
+
+ /* Actually, the required msb for private keys */
+ grp->nbits = 447;
+
+cleanup:
+ mbedtls_mpi_free( &Ns );
+ if( ret != 0 )
+ mbedtls_ecp_group_free( grp );
+
+ return( ret );
+}
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
/*
* Set a group using well-known domain parameters
*/
@@ -767,6 +821,12 @@ int mbedtls_ecp_group_load( mbedtls_ecp_group *grp, mbedtls_ecp_group_id id )
return( ecp_use_curve25519( grp ) );
#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+ case MBEDTLS_ECP_DP_CURVE448:
+ grp->modp = ecp_mod_p448;
+ return( ecp_use_curve448( grp ) );
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
default:
mbedtls_ecp_group_free( grp );
return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
@@ -1176,7 +1236,7 @@ static int ecp_mod_p255( mbedtls_mpi *N )
M.s = 1;
M.n = N->n - ( P255_WIDTH - 1 );
if( M.n > P255_WIDTH + 1 )
- M.n = P255_WIDTH + 1;
+ return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
M.p = Mp;
memset( Mp, 0, sizeof Mp );
memcpy( Mp, N->p + P255_WIDTH - 1, M.n * sizeof( mbedtls_mpi_uint ) );
@@ -1197,6 +1257,77 @@ cleanup:
}
#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+
+/* Size of p448 in terms of mbedtls_mpi_uint */
+#define P448_WIDTH ( 448 / 8 / sizeof( mbedtls_mpi_uint ) )
+
+/* Number of limbs fully occupied by 2^224 (max), and limbs used by it (min) */
+#define DIV_ROUND_UP( X, Y ) ( ( ( X ) + ( Y ) - 1 ) / ( Y ) )
+#define P224_WIDTH_MIN ( 28 / sizeof( mbedtls_mpi_uint ) )
+#define P224_WIDTH_MAX DIV_ROUND_UP( 28, sizeof( mbedtls_mpi_uint ) )
+#define P224_UNUSED_BITS ( ( P224_WIDTH_MAX * sizeof( mbedtls_mpi_uint ) * 8 ) - 224 )
+
+/*
+ * Fast quasi-reduction modulo p448 = 2^448 - 2^224 - 1
+ * Write N as A0 + 2^448 A1 and A1 as B0 + 2^224 B1, and return
+ * A0 + A1 + B1 + (B0 + B1) * 2^224. This is different to the reference
+ * implementation of Curve448, which uses its own special 56-bit limbs rather
+ * than a generic bignum library. We could squeeze some extra speed out on
+ * 32-bit machines by splitting N up into 32-bit limbs and doing the
+ * arithmetic using the limbs directly as we do for the NIST primes above,
+ * but for 64-bit targets it should use half the number of operations if we do
+ * the reduction with 224-bit limbs, since mpi_add_mpi will then use 64-bit adds.
+ */
+static int ecp_mod_p448( mbedtls_mpi *N )
+{
+ int ret;
+ size_t i;
+ mbedtls_mpi M, Q;
+ mbedtls_mpi_uint Mp[P448_WIDTH + 1], Qp[P448_WIDTH];
+
+ if( N->n <= P448_WIDTH )
+ return( 0 );
+
+ /* M = A1 */
+ M.s = 1;
+ M.n = N->n - ( P448_WIDTH );
+ if( M.n > P448_WIDTH )
+ /* Shouldn't be called with N larger than 2^896! */
+ return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+ M.p = Mp;
+ memset( Mp, 0, sizeof( Mp ) );
+ memcpy( Mp, N->p + P448_WIDTH, M.n * sizeof( mbedtls_mpi_uint ) );
+
+ /* N = A0 */
+ for( i = P448_WIDTH; i < N->n; i++ )
+ N->p[i] = 0;
+
+ /* N += A1 */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &M ) );
+
+ /* Q = B1, N += B1 */
+ Q = M;
+ Q.p = Qp;
+ memcpy( Qp, Mp, sizeof( Qp ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Q, 224 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &Q ) );
+
+ /* M = (B0 + B1) * 2^224, N += M */
+ if( sizeof( mbedtls_mpi_uint ) > 4 )
+ Mp[P224_WIDTH_MIN] &= ( (mbedtls_mpi_uint)-1 ) >> ( P224_UNUSED_BITS );
+ for( i = P224_WIDTH_MAX; i < M.n; ++i )
+ Mp[i] = 0;
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &M, &M, &Q ) );
+ M.n = P448_WIDTH + 1; /* Make room for shifted carry bit from the addition */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &M, 224 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( N, N, &M ) );
+
+cleanup:
+ return( ret );
+}
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \
defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \
defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
diff --git a/thirdparty/mbedtls/library/entropy.c b/thirdparty/mbedtls/library/entropy.c
index e17512e779..f8db1a5503 100644
--- a/thirdparty/mbedtls/library/entropy.c
+++ b/thirdparty/mbedtls/library/entropy.c
@@ -35,6 +35,7 @@
#include "mbedtls/entropy.h"
#include "mbedtls/entropy_poll.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -59,11 +60,6 @@
#include "mbedtls/havege.h"
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
#define ENTROPY_MAX_LOOP 256 /**< Maximum amount to loop before error */
void mbedtls_entropy_init( mbedtls_entropy_context *ctx )
@@ -140,7 +136,7 @@ void mbedtls_entropy_free( mbedtls_entropy_context *ctx )
ctx->initial_entropy_run = 0;
#endif
ctx->source_count = 0;
- mbedtls_zeroize( ctx->source, sizeof( ctx->source ) );
+ mbedtls_platform_zeroize( ctx->source, sizeof( ctx->source ) );
ctx->accumulator_started = 0;
}
@@ -232,7 +228,7 @@ static int entropy_update( mbedtls_entropy_context *ctx, unsigned char source_id
#endif
cleanup:
- mbedtls_zeroize( tmp, sizeof( tmp ) );
+ mbedtls_platform_zeroize( tmp, sizeof( tmp ) );
return( ret );
}
@@ -300,7 +296,7 @@ static int entropy_gather_internal( mbedtls_entropy_context *ctx )
ret = MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE;
cleanup:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
return( ret );
}
@@ -433,7 +429,7 @@ int mbedtls_entropy_func( void *data, unsigned char *output, size_t len )
ret = 0;
exit:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
#if defined(MBEDTLS_THREADING_C)
if( mbedtls_mutex_unlock( &ctx->mutex ) != 0 )
@@ -486,7 +482,7 @@ int mbedtls_entropy_write_seed_file( mbedtls_entropy_context *ctx, const char *p
ret = 0;
exit:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
fclose( f );
return( ret );
@@ -516,7 +512,7 @@ int mbedtls_entropy_update_seed_file( mbedtls_entropy_context *ctx, const char *
fclose( f );
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
if( ret != 0 )
return( ret );
diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
index ed350735d0..cefe882d2a 100644
--- a/thirdparty/mbedtls/library/entropy_poll.c
+++ b/thirdparty/mbedtls/library/entropy_poll.c
@@ -44,7 +44,7 @@
#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
- !defined(__APPLE__) && !defined(_WIN32)
+ !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__)
#error "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in config.h"
#endif
diff --git a/thirdparty/mbedtls/library/error.c b/thirdparty/mbedtls/library/error.c
index 0292480aee..8818054c56 100644
--- a/thirdparty/mbedtls/library/error.c
+++ b/thirdparty/mbedtls/library/error.c
@@ -49,6 +49,10 @@
#include "mbedtls/arc4.h"
#endif
+#if defined(MBEDTLS_ARIA_C)
+#include "mbedtls/aria.h"
+#endif
+
#if defined(MBEDTLS_BASE64_C)
#include "mbedtls/base64.h"
#endif
@@ -256,19 +260,19 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
if( use_ret == -(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) )
mbedtls_snprintf( buf, buflen, "ECP - The buffer is too small to write to" );
if( use_ret == -(MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) )
- mbedtls_snprintf( buf, buflen, "ECP - Requested curve not available" );
+ mbedtls_snprintf( buf, buflen, "ECP - The requested feature is not available, for example, the requested curve is not supported" );
if( use_ret == -(MBEDTLS_ERR_ECP_VERIFY_FAILED) )
mbedtls_snprintf( buf, buflen, "ECP - The signature is not valid" );
if( use_ret == -(MBEDTLS_ERR_ECP_ALLOC_FAILED) )
mbedtls_snprintf( buf, buflen, "ECP - Memory allocation failed" );
if( use_ret == -(MBEDTLS_ERR_ECP_RANDOM_FAILED) )
- mbedtls_snprintf( buf, buflen, "ECP - Generation of random value, such as (ephemeral) key, failed" );
+ mbedtls_snprintf( buf, buflen, "ECP - Generation of random value, such as ephemeral key, failed" );
if( use_ret == -(MBEDTLS_ERR_ECP_INVALID_KEY) )
mbedtls_snprintf( buf, buflen, "ECP - Invalid private or public key" );
if( use_ret == -(MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH) )
- mbedtls_snprintf( buf, buflen, "ECP - Signature is valid but shorter than the user-supplied length" );
+ mbedtls_snprintf( buf, buflen, "ECP - The buffer contains a valid signature followed by more data" );
if( use_ret == -(MBEDTLS_ERR_ECP_HW_ACCEL_FAILED) )
- mbedtls_snprintf( buf, buflen, "ECP - ECP hardware accelerator failed" );
+ mbedtls_snprintf( buf, buflen, "ECP - The ECP hardware accelerator failed" );
#endif /* MBEDTLS_ECP_C */
#if defined(MBEDTLS_MD_C)
@@ -333,7 +337,7 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
if( use_ret == -(MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE) )
mbedtls_snprintf( buf, buflen, "PK - Unavailable feature, e.g. RSA disabled for RSA key" );
if( use_ret == -(MBEDTLS_ERR_PK_SIG_LEN_MISMATCH) )
- mbedtls_snprintf( buf, buflen, "PK - The signature is valid but its length is less than expected" );
+ mbedtls_snprintf( buf, buflen, "PK - The buffer contains a valid signature followed by more data" );
if( use_ret == -(MBEDTLS_ERR_PK_HW_ACCEL_FAILED) )
mbedtls_snprintf( buf, buflen, "PK - PK hardware accelerator failed" );
#endif /* MBEDTLS_PK_C */
@@ -478,7 +482,7 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
if( use_ret == -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) )
mbedtls_snprintf( buf, buflen, "SSL - None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages)" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_READ) )
- mbedtls_snprintf( buf, buflen, "SSL - Connection requires a read call" );
+ mbedtls_snprintf( buf, buflen, "SSL - No data of requested type currently available on underlying transport" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_WRITE) )
mbedtls_snprintf( buf, buflen, "SSL - Connection requires a write call" );
if( use_ret == -(MBEDTLS_ERR_SSL_TIMEOUT) )
@@ -491,6 +495,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "SSL - The alert message received indicates a non-fatal error" );
if( use_ret == -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH) )
mbedtls_snprintf( buf, buflen, "SSL - Couldn't set the hash for verifying CertificateVerify" );
+ if( use_ret == -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING) )
+ mbedtls_snprintf( buf, buflen, "SSL - Internal-only message signaling that further message-processing should be done" );
#endif /* MBEDTLS_SSL_TLS_C */
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
@@ -570,6 +576,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "AES - Invalid key length" );
if( use_ret == -(MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH) )
mbedtls_snprintf( buf, buflen, "AES - Invalid data input length" );
+ if( use_ret == -(MBEDTLS_ERR_AES_BAD_INPUT_DATA) )
+ mbedtls_snprintf( buf, buflen, "AES - Invalid input data" );
if( use_ret == -(MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE) )
mbedtls_snprintf( buf, buflen, "AES - Feature not available. For example, an unsupported AES key size" );
if( use_ret == -(MBEDTLS_ERR_AES_HW_ACCEL_FAILED) )
@@ -581,6 +589,17 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "ARC4 - ARC4 hardware accelerator failed" );
#endif /* MBEDTLS_ARC4_C */
+#if defined(MBEDTLS_ARIA_C)
+ if( use_ret == -(MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH) )
+ mbedtls_snprintf( buf, buflen, "ARIA - Invalid key length" );
+ if( use_ret == -(MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH) )
+ mbedtls_snprintf( buf, buflen, "ARIA - Invalid data input length" );
+ if( use_ret == -(MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE) )
+ mbedtls_snprintf( buf, buflen, "ARIA - Feature not available. For example, an unsupported ARIA key size" );
+ if( use_ret == -(MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED) )
+ mbedtls_snprintf( buf, buflen, "ARIA - ARIA hardware accelerator failed" );
+#endif /* MBEDTLS_ARIA_C */
+
#if defined(MBEDTLS_ASN1_PARSE_C)
if( use_ret == -(MBEDTLS_ERR_ASN1_OUT_OF_DATA) )
mbedtls_snprintf( buf, buflen, "ASN1 - Out of data when parsing an ASN1 data structure" );
@@ -745,6 +764,10 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen )
mbedtls_snprintf( buf, buflen, "NET - Buffer is too small to hold the data" );
if( use_ret == -(MBEDTLS_ERR_NET_INVALID_CONTEXT) )
mbedtls_snprintf( buf, buflen, "NET - The context is invalid, eg because it was free()ed" );
+ if( use_ret == -(MBEDTLS_ERR_NET_POLL_FAILED) )
+ mbedtls_snprintf( buf, buflen, "NET - Polling the net context failed" );
+ if( use_ret == -(MBEDTLS_ERR_NET_BAD_INPUT_DATA) )
+ mbedtls_snprintf( buf, buflen, "NET - Input invalid" );
#endif /* MBEDTLS_NET_C */
#if defined(MBEDTLS_OID_C)
diff --git a/thirdparty/mbedtls/library/gcm.c b/thirdparty/mbedtls/library/gcm.c
index 294a86d3d4..57b027933d 100644
--- a/thirdparty/mbedtls/library/gcm.c
+++ b/thirdparty/mbedtls/library/gcm.c
@@ -38,6 +38,7 @@
#if defined(MBEDTLS_GCM_C)
#include "mbedtls/gcm.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -80,11 +81,6 @@
}
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Initialize a context
*/
@@ -498,7 +494,7 @@ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx,
if( diff != 0 )
{
- mbedtls_zeroize( output, length );
+ mbedtls_platform_zeroize( output, length );
return( MBEDTLS_ERR_GCM_AUTH_FAILED );
}
@@ -508,7 +504,7 @@ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx,
void mbedtls_gcm_free( mbedtls_gcm_context *ctx )
{
mbedtls_cipher_free( &ctx->cipher_ctx );
- mbedtls_zeroize( ctx, sizeof( mbedtls_gcm_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_gcm_context ) );
}
#endif /* !MBEDTLS_GCM_ALT */
diff --git a/thirdparty/mbedtls/library/havege.c b/thirdparty/mbedtls/library/havege.c
index 2b75ef7bd8..4dcac02875 100644
--- a/thirdparty/mbedtls/library/havege.c
+++ b/thirdparty/mbedtls/library/havege.c
@@ -36,14 +36,10 @@
#include "mbedtls/havege.h"
#include "mbedtls/timing.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/* ------------------------------------------------------------------------
* On average, one iteration accesses two 8-word blocks in the havege WALK
* table, and generates 16 words in the RES array.
@@ -208,7 +204,7 @@ void mbedtls_havege_free( mbedtls_havege_state *hs )
if( hs == NULL )
return;
- mbedtls_zeroize( hs, sizeof( mbedtls_havege_state ) );
+ mbedtls_platform_zeroize( hs, sizeof( mbedtls_havege_state ) );
}
/*
diff --git a/thirdparty/mbedtls/library/hmac_drbg.c b/thirdparty/mbedtls/library/hmac_drbg.c
index 24c609e9ce..dad55ff861 100644
--- a/thirdparty/mbedtls/library/hmac_drbg.c
+++ b/thirdparty/mbedtls/library/hmac_drbg.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_HMAC_DRBG_C)
#include "mbedtls/hmac_drbg.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -50,11 +51,6 @@
#endif /* MBEDTLS_SELF_TEST */
#endif /* MBEDTLS_PLATFORM_C */
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* HMAC_DRBG context initialization
*/
@@ -338,7 +334,7 @@ void mbedtls_hmac_drbg_free( mbedtls_hmac_drbg_context *ctx )
mbedtls_mutex_free( &ctx->mutex );
#endif
mbedtls_md_free( &ctx->md_ctx );
- mbedtls_zeroize( ctx, sizeof( mbedtls_hmac_drbg_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_hmac_drbg_context ) );
}
#if defined(MBEDTLS_FS_IO)
@@ -364,7 +360,7 @@ int mbedtls_hmac_drbg_write_seed_file( mbedtls_hmac_drbg_context *ctx, const cha
exit:
fclose( f );
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
return( ret );
}
@@ -396,7 +392,7 @@ int mbedtls_hmac_drbg_update_seed_file( mbedtls_hmac_drbg_context *ctx, const ch
fclose( f );
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
if( ret != 0 )
return( ret );
diff --git a/thirdparty/mbedtls/library/md.c b/thirdparty/mbedtls/library/md.c
index 00249af78b..303cdcbeeb 100644
--- a/thirdparty/mbedtls/library/md.c
+++ b/thirdparty/mbedtls/library/md.c
@@ -33,6 +33,7 @@
#include "mbedtls/md.h"
#include "mbedtls/md_internal.h"
+#include "mbedtls/platform_util.h"
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
@@ -48,11 +49,6 @@
#include <stdio.h>
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Reminder: update profiles in x509_crt.c when adding a new hash!
*/
@@ -193,11 +189,12 @@ void mbedtls_md_free( mbedtls_md_context_t *ctx )
if( ctx->hmac_ctx != NULL )
{
- mbedtls_zeroize( ctx->hmac_ctx, 2 * ctx->md_info->block_size );
+ mbedtls_platform_zeroize( ctx->hmac_ctx,
+ 2 * ctx->md_info->block_size );
mbedtls_free( ctx->hmac_ctx );
}
- mbedtls_zeroize( ctx, sizeof( mbedtls_md_context_t ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_md_context_t ) );
}
int mbedtls_md_clone( mbedtls_md_context_t *dst,
@@ -311,7 +308,7 @@ int mbedtls_md_file( const mbedtls_md_info_t *md_info, const char *path, unsigne
ret = md_info->finish_func( ctx.md_ctx, output );
cleanup:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
fclose( f );
mbedtls_md_free( &ctx );
@@ -361,7 +358,7 @@ int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key,
goto cleanup;
cleanup:
- mbedtls_zeroize( sum, sizeof( sum ) );
+ mbedtls_platform_zeroize( sum, sizeof( sum ) );
return( ret );
}
diff --git a/thirdparty/mbedtls/library/md2.c b/thirdparty/mbedtls/library/md2.c
index b88aa406af..1c0b3df52d 100644
--- a/thirdparty/mbedtls/library/md2.c
+++ b/thirdparty/mbedtls/library/md2.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_MD2_C)
#include "mbedtls/md2.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -48,11 +49,6 @@
#if !defined(MBEDTLS_MD2_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
static const unsigned char PI_SUBST[256] =
{
0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36,
@@ -93,7 +89,7 @@ void mbedtls_md2_free( mbedtls_md2_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_md2_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_md2_context ) );
}
void mbedtls_md2_clone( mbedtls_md2_context *dst,
diff --git a/thirdparty/mbedtls/library/md4.c b/thirdparty/mbedtls/library/md4.c
index ba704f58e8..3f8ddff31d 100644
--- a/thirdparty/mbedtls/library/md4.c
+++ b/thirdparty/mbedtls/library/md4.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_MD4_C)
#include "mbedtls/md4.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -48,11 +49,6 @@
#if !defined(MBEDTLS_MD4_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (little endian)
*/
@@ -86,7 +82,7 @@ void mbedtls_md4_free( mbedtls_md4_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_md4_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_md4_context ) );
}
void mbedtls_md4_clone( mbedtls_md4_context *dst,
diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c
index 8440ebffcf..8238c2b81a 100644
--- a/thirdparty/mbedtls/library/md5.c
+++ b/thirdparty/mbedtls/library/md5.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_MD5_C)
#include "mbedtls/md5.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -47,11 +48,6 @@
#if !defined(MBEDTLS_MD5_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (little endian)
*/
@@ -85,7 +81,7 @@ void mbedtls_md5_free( mbedtls_md5_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_md5_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_md5_context ) );
}
void mbedtls_md5_clone( mbedtls_md5_context *dst,
diff --git a/thirdparty/mbedtls/library/memory_buffer_alloc.c b/thirdparty/mbedtls/library/memory_buffer_alloc.c
index 821ae2c708..ceaeda1e73 100644
--- a/thirdparty/mbedtls/library/memory_buffer_alloc.c
+++ b/thirdparty/mbedtls/library/memory_buffer_alloc.c
@@ -31,6 +31,7 @@
/* No need for the header guard as MBEDTLS_MEMORY_BUFFER_ALLOC_C
is dependent upon MBEDTLS_PLATFORM_C */
#include "mbedtls/platform.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -42,11 +43,6 @@
#include "mbedtls/threading.h"
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
#define MAGIC1 0xFF00AA55
#define MAGIC2 0xEE119966
#define MAX_BT 20
@@ -612,7 +608,7 @@ void mbedtls_memory_buffer_alloc_free( void )
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &heap.mutex );
#endif
- mbedtls_zeroize( &heap, sizeof(buffer_alloc_ctx) );
+ mbedtls_platform_zeroize( &heap, sizeof(buffer_alloc_ctx) );
}
#if defined(MBEDTLS_SELF_TEST)
diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c
index 2fb548caa9..9b0a375fb7 100644
--- a/thirdparty/mbedtls/library/net_sockets.c
+++ b/thirdparty/mbedtls/library/net_sockets.c
@@ -28,7 +28,7 @@
#if defined(MBEDTLS_NET_C)
#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
- !defined(__APPLE__) && !defined(_WIN32)
+ !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__)
#error "This module only works on Unix and Windows, see MBEDTLS_NET_C in config.h"
#endif
@@ -45,6 +45,8 @@
#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \
!defined(EFI32)
+#define IS_EINTR( ret ) ( ( ret ) == WSAEINTR )
+
/* GODOT ADDITION */
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
#undef _WIN32_WINNT
@@ -85,6 +87,8 @@ static int wsa_init_done = 0;
#include <netdb.h>
#include <errno.h>
+#define IS_EINTR( ret ) ( ( ret ) == EINTR )
+
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
/* Some MS functions want int and MSVC warns if we pass size_t,
@@ -274,7 +278,7 @@ static int net_would_block( const mbedtls_net_context *ctx )
static int net_would_block( const mbedtls_net_context *ctx )
{
int err = errno;
-
+
/*
* Never return 'WOULD BLOCK' on a non-blocking socket
*/
@@ -442,6 +446,72 @@ int mbedtls_net_set_nonblock( mbedtls_net_context *ctx )
}
/*
+ * Check if data is available on the socket
+ */
+
+int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout )
+{
+ int ret;
+ struct timeval tv;
+
+ fd_set read_fds;
+ fd_set write_fds;
+
+ int fd = ctx->fd;
+
+ if( fd < 0 )
+ return( MBEDTLS_ERR_NET_INVALID_CONTEXT );
+
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+ /* Ensure that memory sanitizers consider read_fds and write_fds as
+ * initialized even on platforms such as Glibc/x86_64 where FD_ZERO
+ * is implemented in assembly. */
+ memset( &read_fds, 0, sizeof( read_fds ) );
+ memset( &write_fds, 0, sizeof( write_fds ) );
+#endif
+#endif
+
+ FD_ZERO( &read_fds );
+ if( rw & MBEDTLS_NET_POLL_READ )
+ {
+ rw &= ~MBEDTLS_NET_POLL_READ;
+ FD_SET( fd, &read_fds );
+ }
+
+ FD_ZERO( &write_fds );
+ if( rw & MBEDTLS_NET_POLL_WRITE )
+ {
+ rw &= ~MBEDTLS_NET_POLL_WRITE;
+ FD_SET( fd, &write_fds );
+ }
+
+ if( rw != 0 )
+ return( MBEDTLS_ERR_NET_BAD_INPUT_DATA );
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = ( timeout % 1000 ) * 1000;
+
+ do
+ {
+ ret = select( fd + 1, &read_fds, &write_fds, NULL,
+ timeout == (uint32_t) -1 ? NULL : &tv );
+ }
+ while( IS_EINTR( ret ) );
+
+ if( ret < 0 )
+ return( MBEDTLS_ERR_NET_POLL_FAILED );
+
+ ret = 0;
+ if( FD_ISSET( fd, &read_fds ) )
+ ret |= MBEDTLS_NET_POLL_READ;
+ if( FD_ISSET( fd, &write_fds ) )
+ ret |= MBEDTLS_NET_POLL_WRITE;
+
+ return( ret );
+}
+
+/*
* Portable usleep helper
*/
void mbedtls_net_usleep( unsigned long usec )
@@ -500,8 +570,8 @@ int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
/*
* Read at most 'len' characters, blocking for at most 'timeout' ms
*/
-int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len,
- uint32_t timeout )
+int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf,
+ size_t len, uint32_t timeout )
{
int ret;
struct timeval tv;
diff --git a/thirdparty/mbedtls/library/pem.c b/thirdparty/mbedtls/library/pem.c
index ac86d7e479..6069a23dec 100644
--- a/thirdparty/mbedtls/library/pem.c
+++ b/thirdparty/mbedtls/library/pem.c
@@ -33,6 +33,7 @@
#include "mbedtls/aes.h"
#include "mbedtls/md5.h"
#include "mbedtls/cipher.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -45,11 +46,6 @@
#endif
#if defined(MBEDTLS_PEM_PARSE_C)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
void mbedtls_pem_init( mbedtls_pem_context *ctx )
{
memset( ctx, 0, sizeof( mbedtls_pem_context ) );
@@ -135,7 +131,7 @@ static int pem_pbkdf1( unsigned char *key, size_t keylen,
exit:
mbedtls_md5_free( &md5_ctx );
- mbedtls_zeroize( md5sum, 16 );
+ mbedtls_platform_zeroize( md5sum, 16 );
return( ret );
}
@@ -164,7 +160,7 @@ static int pem_des_decrypt( unsigned char des_iv[8],
exit:
mbedtls_des_free( &des_ctx );
- mbedtls_zeroize( des_key, 8 );
+ mbedtls_platform_zeroize( des_key, 8 );
return( ret );
}
@@ -192,7 +188,7 @@ static int pem_des3_decrypt( unsigned char des3_iv[8],
exit:
mbedtls_des3_free( &des3_ctx );
- mbedtls_zeroize( des3_key, 24 );
+ mbedtls_platform_zeroize( des3_key, 24 );
return( ret );
}
@@ -222,7 +218,7 @@ static int pem_aes_decrypt( unsigned char aes_iv[16], unsigned int keylen,
exit:
mbedtls_aes_free( &aes_ctx );
- mbedtls_zeroize( aes_key, keylen );
+ mbedtls_platform_zeroize( aes_key, keylen );
return( ret );
}
@@ -359,7 +355,7 @@ int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const
if( ( ret = mbedtls_base64_decode( buf, len, &len, s1, s2 - s1 ) ) != 0 )
{
- mbedtls_zeroize( buf, len );
+ mbedtls_platform_zeroize( buf, len );
mbedtls_free( buf );
return( MBEDTLS_ERR_PEM_INVALID_DATA + ret );
}
@@ -370,7 +366,7 @@ int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const
( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) )
if( pwd == NULL )
{
- mbedtls_zeroize( buf, len );
+ mbedtls_platform_zeroize( buf, len );
mbedtls_free( buf );
return( MBEDTLS_ERR_PEM_PASSWORD_REQUIRED );
}
@@ -403,16 +399,16 @@ int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const
* The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3
* length bytes (allow 4 to be sure) in all known use cases.
*
- * Use that as heurisitic to try detecting password mismatchs.
+ * Use that as a heuristic to try to detect password mismatches.
*/
if( len <= 2 || buf[0] != 0x30 || buf[1] > 0x83 )
{
- mbedtls_zeroize( buf, len );
+ mbedtls_platform_zeroize( buf, len );
mbedtls_free( buf );
return( MBEDTLS_ERR_PEM_PASSWORD_MISMATCH );
}
#else
- mbedtls_zeroize( buf, len );
+ mbedtls_platform_zeroize( buf, len );
mbedtls_free( buf );
return( MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE );
#endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC &&
@@ -428,11 +424,11 @@ int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const
void mbedtls_pem_free( mbedtls_pem_context *ctx )
{
if( ctx->buf != NULL )
- mbedtls_zeroize( ctx->buf, ctx->buflen );
+ mbedtls_platform_zeroize( ctx->buf, ctx->buflen );
mbedtls_free( ctx->buf );
mbedtls_free( ctx->info );
- mbedtls_zeroize( ctx, sizeof( mbedtls_pem_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_pem_context ) );
}
#endif /* MBEDTLS_PEM_PARSE_C */
diff --git a/thirdparty/mbedtls/library/pk.c b/thirdparty/mbedtls/library/pk.c
index b52c73fbc6..f05b139e3f 100644
--- a/thirdparty/mbedtls/library/pk.c
+++ b/thirdparty/mbedtls/library/pk.c
@@ -29,6 +29,8 @@
#include "mbedtls/pk.h"
#include "mbedtls/pk_internal.h"
+#include "mbedtls/platform_util.h"
+
#if defined(MBEDTLS_RSA_C)
#include "mbedtls/rsa.h"
#endif
@@ -42,11 +44,6 @@
#include <limits.h>
#include <stdint.h>
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Initialise a mbedtls_pk_context
*/
@@ -69,7 +66,7 @@ void mbedtls_pk_free( mbedtls_pk_context *ctx )
ctx->pk_info->ctx_free_func( ctx->pk_ctx );
- mbedtls_zeroize( ctx, sizeof( mbedtls_pk_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_pk_context ) );
}
/*
diff --git a/thirdparty/mbedtls/library/pk_wrap.c b/thirdparty/mbedtls/library/pk_wrap.c
index a4bb35fc8f..2c7d2d79b8 100644
--- a/thirdparty/mbedtls/library/pk_wrap.c
+++ b/thirdparty/mbedtls/library/pk_wrap.c
@@ -41,6 +41,10 @@
#include "mbedtls/ecdsa.h"
#endif
+#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
+#include "mbedtls/platform_util.h"
+#endif
+
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
@@ -52,13 +56,6 @@
#include <limits.h>
#include <stdint.h>
-#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-#endif
-
#if defined(MBEDTLS_RSA_C)
static int rsa_can_do( mbedtls_pk_type_t type )
{
@@ -93,6 +90,11 @@ static int rsa_verify_wrap( void *ctx, mbedtls_md_type_t md_alg,
(unsigned int) hash_len, hash, sig ) ) != 0 )
return( ret );
+ /* The buffer contains a valid signature followed by extra data.
+ * We have a special error code for that so that so that callers can
+ * use mbedtls_pk_verify() to check "Does the buffer start with a
+ * valid signature?" and not just "Does the buffer contain a valid
+ * signature?". */
if( sig_len > rsa_len )
return( MBEDTLS_ERR_PK_SIG_LEN_MISMATCH );
@@ -493,7 +495,7 @@ static void *rsa_alt_alloc_wrap( void )
static void rsa_alt_free_wrap( void *ctx )
{
- mbedtls_zeroize( ctx, sizeof( mbedtls_rsa_alt_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_rsa_alt_context ) );
mbedtls_free( ctx );
}
diff --git a/thirdparty/mbedtls/library/pkcs12.c b/thirdparty/mbedtls/library/pkcs12.c
index c603a13577..16a15cb63e 100644
--- a/thirdparty/mbedtls/library/pkcs12.c
+++ b/thirdparty/mbedtls/library/pkcs12.c
@@ -36,6 +36,7 @@
#include "mbedtls/pkcs12.h"
#include "mbedtls/asn1.h"
#include "mbedtls/cipher.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -47,11 +48,6 @@
#include "mbedtls/des.h"
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
static int pkcs12_parse_pbe_params( mbedtls_asn1_buf *params,
mbedtls_asn1_buf *salt, int *iterations )
{
@@ -166,7 +162,7 @@ int mbedtls_pkcs12_pbe_sha1_rc4_128( mbedtls_asn1_buf *pbe_params, int mode,
goto exit;
exit:
- mbedtls_zeroize( key, sizeof( key ) );
+ mbedtls_platform_zeroize( key, sizeof( key ) );
mbedtls_arc4_free( &ctx );
return( ret );
@@ -223,8 +219,8 @@ int mbedtls_pkcs12_pbe( mbedtls_asn1_buf *pbe_params, int mode,
ret = MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH;
exit:
- mbedtls_zeroize( key, sizeof( key ) );
- mbedtls_zeroize( iv, sizeof( iv ) );
+ mbedtls_platform_zeroize( key, sizeof( key ) );
+ mbedtls_platform_zeroize( iv, sizeof( iv ) );
mbedtls_cipher_free( &cipher_ctx );
return( ret );
@@ -352,10 +348,10 @@ int mbedtls_pkcs12_derivation( unsigned char *data, size_t datalen,
ret = 0;
exit:
- mbedtls_zeroize( salt_block, sizeof( salt_block ) );
- mbedtls_zeroize( pwd_block, sizeof( pwd_block ) );
- mbedtls_zeroize( hash_block, sizeof( hash_block ) );
- mbedtls_zeroize( hash_output, sizeof( hash_output ) );
+ mbedtls_platform_zeroize( salt_block, sizeof( salt_block ) );
+ mbedtls_platform_zeroize( pwd_block, sizeof( pwd_block ) );
+ mbedtls_platform_zeroize( hash_block, sizeof( hash_block ) );
+ mbedtls_platform_zeroize( hash_output, sizeof( hash_output ) );
mbedtls_md_free( &md_ctx );
diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c
index 95f44fa98b..440a174b5b 100644
--- a/thirdparty/mbedtls/library/pkcs5.c
+++ b/thirdparty/mbedtls/library/pkcs5.c
@@ -38,9 +38,12 @@
#if defined(MBEDTLS_PKCS5_C)
#include "mbedtls/pkcs5.h"
+
+#if defined(MBEDTLS_ASN1_PARSE_C)
#include "mbedtls/asn1.h"
#include "mbedtls/cipher.h"
#include "mbedtls/oid.h"
+#endif /* MBEDTLS_ASN1_PARSE_C */
#include <string.h>
@@ -51,6 +54,22 @@
#define mbedtls_printf printf
#endif
+#if !defined(MBEDTLS_ASN1_PARSE_C)
+int mbedtls_pkcs5_pbes2( const mbedtls_asn1_buf *pbe_params, int mode,
+ const unsigned char *pwd, size_t pwdlen,
+ const unsigned char *data, size_t datalen,
+ unsigned char *output )
+{
+ ((void) pbe_params);
+ ((void) mode);
+ ((void) pwd);
+ ((void) pwdlen);
+ ((void) data);
+ ((void) datalen);
+ ((void) output);
+ return( MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE );
+}
+#else
static int pkcs5_parse_pbkdf2_params( const mbedtls_asn1_buf *params,
mbedtls_asn1_buf *salt, int *iterations,
int *keylen, mbedtls_md_type_t *md_type )
@@ -211,6 +230,7 @@ exit:
return( ret );
}
+#endif /* MBEDTLS_ASN1_PARSE_C */
int mbedtls_pkcs5_pbkdf2_hmac( mbedtls_md_context_t *ctx, const unsigned char *password,
size_t plen, const unsigned char *salt, size_t slen,
diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c
index 9022db2f93..ccb7f5409d 100644
--- a/thirdparty/mbedtls/library/pkparse.c
+++ b/thirdparty/mbedtls/library/pkparse.c
@@ -30,6 +30,7 @@
#include "mbedtls/pk.h"
#include "mbedtls/asn1.h"
#include "mbedtls/oid.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -60,14 +61,6 @@
#define mbedtls_free free
#endif
-#if defined(MBEDTLS_FS_IO) || \
- defined(MBEDTLS_PKCS12_C) || defined(MBEDTLS_PKCS5_C)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-#endif
-
#if defined(MBEDTLS_FS_IO)
/*
* Load all data from a file into a given buffer.
@@ -105,7 +98,7 @@ int mbedtls_pk_load_file( const char *path, unsigned char **buf, size_t *n )
{
fclose( f );
- mbedtls_zeroize( *buf, *n );
+ mbedtls_platform_zeroize( *buf, *n );
mbedtls_free( *buf );
return( MBEDTLS_ERR_PK_FILE_IO_ERROR );
@@ -140,7 +133,7 @@ int mbedtls_pk_parse_keyfile( mbedtls_pk_context *ctx,
ret = mbedtls_pk_parse_key( ctx, buf, n,
(const unsigned char *) pwd, strlen( pwd ) );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
@@ -160,7 +153,7 @@ int mbedtls_pk_parse_public_keyfile( mbedtls_pk_context *ctx, const char *path )
ret = mbedtls_pk_parse_public_key( ctx, buf, n );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
@@ -861,7 +854,10 @@ static int pk_parse_key_sec1_der( mbedtls_ecp_keypair *eck,
mbedtls_ecp_keypair_free( eck );
return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret );
}
+ }
+ if( p != end )
+ {
/*
* Is 'publickey' present? If not, or if we can't read it (eg because it
* is compressed), create it from the private key.
@@ -1292,7 +1288,7 @@ int mbedtls_pk_parse_key( mbedtls_pk_context *pk,
ret = pk_parse_key_pkcs8_encrypted_der( pk, key_copy, keylen,
pwd, pwdlen );
- mbedtls_zeroize( key_copy, keylen );
+ mbedtls_platform_zeroize( key_copy, keylen );
mbedtls_free( key_copy );
}
diff --git a/thirdparty/mbedtls/library/platform.c b/thirdparty/mbedtls/library/platform.c
index a295f9b9af..9e992875d9 100644
--- a/thirdparty/mbedtls/library/platform.c
+++ b/thirdparty/mbedtls/library/platform.c
@@ -28,14 +28,7 @@
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
-
-#if defined(MBEDTLS_ENTROPY_NV_SEED) && \
- !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) && defined(MBEDTLS_FS_IO)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-#endif
+#include "mbedtls/platform_util.h"
#if defined(MBEDTLS_PLATFORM_MEMORY)
#if !defined(MBEDTLS_PLATFORM_STD_CALLOC)
@@ -241,7 +234,7 @@ int mbedtls_platform_std_nv_seed_read( unsigned char *buf, size_t buf_len )
if( ( n = fread( buf, 1, buf_len, file ) ) != buf_len )
{
fclose( file );
- mbedtls_zeroize( buf, buf_len );
+ mbedtls_platform_zeroize( buf, buf_len );
return( -1 );
}
diff --git a/thirdparty/mbedtls/library/platform_util.c b/thirdparty/mbedtls/library/platform_util.c
new file mode 100644
index 0000000000..1a57de9393
--- /dev/null
+++ b/thirdparty/mbedtls/library/platform_util.c
@@ -0,0 +1,67 @@
+/*
+ * Common and shared functions used by multiple modules in the Mbed TLS
+ * library.
+ *
+ * Copyright (C) 2018, Arm Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of Mbed TLS (https://tls.mbed.org)
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include "mbedtls/platform_util.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#if !defined(MBEDTLS_PLATFORM_ZEROIZE_ALT)
+/*
+ * This implementation should never be optimized out by the compiler
+ *
+ * This implementation for mbedtls_platform_zeroize() was inspired from Colin
+ * Percival's blog article at:
+ *
+ * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html
+ *
+ * It uses a volatile function pointer to the standard memset(). Because the
+ * pointer is volatile the compiler expects it to change at
+ * any time and will not optimize out the call that could potentially perform
+ * other operations on the input buffer instead of just setting it to 0.
+ * Nevertheless, as pointed out by davidtgoldblatt on Hacker News
+ * (refer to http://www.daemonology.net/blog/2014-09-05-erratum.html for
+ * details), optimizations of the following form are still possible:
+ *
+ * if( memset_func != memset )
+ * memset_func( buf, 0, len );
+ *
+ * Note that it is extremely difficult to guarantee that
+ * mbedtls_platform_zeroize() will not be optimized out by aggressive compilers
+ * in a portable way. For this reason, Mbed TLS also provides the configuration
+ * option MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure
+ * mbedtls_platform_zeroize() to use a suitable implementation for their
+ * platform and needs.
+ */
+static void * (* const volatile memset_func)( void *, int, size_t ) = memset;
+
+void mbedtls_platform_zeroize( void *buf, size_t len )
+{
+ memset_func( buf, 0, len );
+}
+#endif /* MBEDTLS_PLATFORM_ZEROIZE_ALT */
diff --git a/thirdparty/mbedtls/library/ripemd160.c b/thirdparty/mbedtls/library/ripemd160.c
index 2ba48b7fdb..bd25ada62c 100644
--- a/thirdparty/mbedtls/library/ripemd160.c
+++ b/thirdparty/mbedtls/library/ripemd160.c
@@ -34,6 +34,7 @@
#if defined(MBEDTLS_RIPEMD160_C)
#include "mbedtls/ripemd160.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -71,11 +72,6 @@
}
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
void mbedtls_ripemd160_init( mbedtls_ripemd160_context *ctx )
{
memset( ctx, 0, sizeof( mbedtls_ripemd160_context ) );
@@ -86,7 +82,7 @@ void mbedtls_ripemd160_free( mbedtls_ripemd160_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_ripemd160_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ripemd160_context ) );
}
void mbedtls_ripemd160_clone( mbedtls_ripemd160_context *dst,
diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c
index c9f7ba91b6..88c1cf1007 100644
--- a/thirdparty/mbedtls/library/rsa.c
+++ b/thirdparty/mbedtls/library/rsa.c
@@ -48,6 +48,7 @@
#include "mbedtls/rsa.h"
#include "mbedtls/rsa_internal.h"
#include "mbedtls/oid.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -70,11 +71,7 @@
#if !defined(MBEDTLS_RSA_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
+#if defined(MBEDTLS_PKCS1_V15)
/* constant-time buffer comparison */
static inline int mbedtls_safer_memcmp( const void *a, const void *b, size_t n )
{
@@ -88,6 +85,7 @@ static inline int mbedtls_safer_memcmp( const void *a, const void *b, size_t n )
return( diff );
}
+#endif /* MBEDTLS_PKCS1_V15 */
int mbedtls_rsa_import( mbedtls_rsa_context *ctx,
const mbedtls_mpi *N,
@@ -493,6 +491,9 @@ size_t mbedtls_rsa_get_len( const mbedtls_rsa_context *ctx )
/*
* Generate an RSA keypair
+ *
+ * This generation method follows the RSA key pair generation procedure of
+ * FIPS 186-4 if 2^16 < exponent < 2^256 and nbits = 2048 or nbits = 3072.
*/
int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
@@ -500,7 +501,7 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
unsigned int nbits, int exponent )
{
int ret;
- mbedtls_mpi H, G;
+ mbedtls_mpi H, G, L;
if( f_rng == NULL || nbits < 128 || exponent < 3 )
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
@@ -510,10 +511,13 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
mbedtls_mpi_init( &H );
mbedtls_mpi_init( &G );
+ mbedtls_mpi_init( &L );
/*
* find primes P and Q with Q < P so that:
- * GCD( E, (P-1)*(Q-1) ) == 1
+ * 1. |P-Q| > 2^( nbits / 2 - 100 )
+ * 2. GCD( E, (P-1)*(Q-1) ) == 1
+ * 3. E^-1 mod LCM(P-1, Q-1) > 2^( nbits / 2 )
*/
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &ctx->E, exponent ) );
@@ -525,40 +529,51 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_mpi_gen_prime( &ctx->Q, nbits >> 1, 0,
f_rng, p_rng ) );
- if( mbedtls_mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 )
+ /* make sure the difference between p and q is not too small (FIPS 186-4 §B.3.3 step 5.4) */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &H, &ctx->P, &ctx->Q ) );
+ if( mbedtls_mpi_bitlen( &H ) <= ( ( nbits >= 200 ) ? ( ( nbits >> 1 ) - 99 ) : 0 ) )
continue;
- MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) );
- if( mbedtls_mpi_bitlen( &ctx->N ) != nbits )
- continue;
-
- if( mbedtls_mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 )
+ /* not required by any standards, but some users rely on the fact that P > Q */
+ if( H.s < 0 )
mbedtls_mpi_swap( &ctx->P, &ctx->Q );
/* Temporarily replace P,Q by P-1, Q-1 */
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &ctx->P, &ctx->P, 1 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &ctx->Q, &ctx->Q, 1 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &H, &ctx->P, &ctx->Q ) );
+
+ /* check GCD( E, (P-1)*(Q-1) ) == 1 (FIPS 186-4 §B.3.1 criterion 2(a)) */
MBEDTLS_MPI_CHK( mbedtls_mpi_gcd( &G, &ctx->E, &H ) );
+ if( mbedtls_mpi_cmp_int( &G, 1 ) != 0 )
+ continue;
+
+ /* compute smallest possible D = E^-1 mod LCM(P-1, Q-1) (FIPS 186-4 §B.3.1 criterion 3(b)) */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_gcd( &G, &ctx->P, &ctx->Q ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_div_mpi( &L, NULL, &H, &G ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &ctx->D, &ctx->E, &L ) );
+
+ if( mbedtls_mpi_bitlen( &ctx->D ) <= ( ( nbits + 1 ) / 2 ) ) // (FIPS 186-4 §B.3.1 criterion 3(a))
+ continue;
+
+ break;
}
- while( mbedtls_mpi_cmp_int( &G, 1 ) != 0 );
+ while( 1 );
/* Restore P,Q */
MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &ctx->P, &ctx->P, 1 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &ctx->Q, &ctx->Q, 1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) );
+
ctx->len = mbedtls_mpi_size( &ctx->N );
+#if !defined(MBEDTLS_RSA_NO_CRT)
/*
- * D = E^-1 mod ((P-1)*(Q-1))
* DP = D mod (P - 1)
* DQ = D mod (Q - 1)
* QP = Q^-1 mod P
*/
-
- MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &ctx->D, &ctx->E, &H ) );
-
-#if !defined(MBEDTLS_RSA_NO_CRT)
MBEDTLS_MPI_CHK( mbedtls_rsa_deduce_crt( &ctx->P, &ctx->Q, &ctx->D,
&ctx->DP, &ctx->DQ, &ctx->QP ) );
#endif /* MBEDTLS_RSA_NO_CRT */
@@ -570,6 +585,7 @@ cleanup:
mbedtls_mpi_free( &H );
mbedtls_mpi_free( &G );
+ mbedtls_mpi_free( &L );
if( ret != 0 )
{
@@ -1040,7 +1056,7 @@ static int mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src,
}
exit:
- mbedtls_zeroize( mask, sizeof( mask ) );
+ mbedtls_platform_zeroize( mask, sizeof( mask ) );
return( ret );
}
@@ -1354,8 +1370,8 @@ int mbedtls_rsa_rsaes_oaep_decrypt( mbedtls_rsa_context *ctx,
ret = 0;
cleanup:
- mbedtls_zeroize( buf, sizeof( buf ) );
- mbedtls_zeroize( lhash, sizeof( lhash ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( lhash, sizeof( lhash ) );
return( ret );
}
@@ -1452,7 +1468,7 @@ int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
ret = 0;
cleanup:
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
return( ret );
}
@@ -1583,7 +1599,7 @@ int mbedtls_rsa_rsassa_pss_sign( mbedtls_rsa_context *ctx,
p += hlen;
*p++ = 0xBC;
- mbedtls_zeroize( salt, sizeof( salt ) );
+ mbedtls_platform_zeroize( salt, sizeof( salt ) );
exit:
mbedtls_md_free( &md_ctx );
@@ -1725,7 +1741,7 @@ static int rsa_rsassa_pkcs1_v15_encode( mbedtls_md_type_t md_alg,
* after the initial bounds check. */
if( p != dst + dst_len )
{
- mbedtls_zeroize( dst, dst_len );
+ mbedtls_platform_zeroize( dst, dst_len );
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
@@ -2062,13 +2078,13 @@ cleanup:
if( encoded != NULL )
{
- mbedtls_zeroize( encoded, sig_len );
+ mbedtls_platform_zeroize( encoded, sig_len );
mbedtls_free( encoded );
}
if( encoded_expected != NULL )
{
- mbedtls_zeroize( encoded_expected, sig_len );
+ mbedtls_platform_zeroize( encoded_expected, sig_len );
mbedtls_free( encoded_expected );
}
diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c
index 1f29a0fbf8..1587de4805 100644
--- a/thirdparty/mbedtls/library/sha1.c
+++ b/thirdparty/mbedtls/library/sha1.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_SHA1_C)
#include "mbedtls/sha1.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -47,11 +48,6 @@
#if !defined(MBEDTLS_SHA1_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -85,7 +81,7 @@ void mbedtls_sha1_free( mbedtls_sha1_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_sha1_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_sha1_context ) );
}
void mbedtls_sha1_clone( mbedtls_sha1_context *dst,
diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c
index f39bcbab6c..695485d847 100644
--- a/thirdparty/mbedtls/library/sha256.c
+++ b/thirdparty/mbedtls/library/sha256.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_SHA256_C)
#include "mbedtls/sha256.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -50,11 +51,6 @@
#if !defined(MBEDTLS_SHA256_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -88,7 +84,7 @@ void mbedtls_sha256_free( mbedtls_sha256_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_sha256_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_sha256_context ) );
}
void mbedtls_sha256_clone( mbedtls_sha256_context *dst,
diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c
index 97cee07c56..6de94e99b4 100644
--- a/thirdparty/mbedtls/library/sha512.c
+++ b/thirdparty/mbedtls/library/sha512.c
@@ -33,6 +33,7 @@
#if defined(MBEDTLS_SHA512_C)
#include "mbedtls/sha512.h"
+#include "mbedtls/platform_util.h"
#if defined(_MSC_VER) || defined(__WATCOMC__)
#define UL64(x) x##ui64
@@ -56,11 +57,6 @@
#if !defined(MBEDTLS_SHA512_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* 64-bit integer manipulation macros (big endian)
*/
@@ -102,7 +98,7 @@ void mbedtls_sha512_free( mbedtls_sha512_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_sha512_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_sha512_context ) );
}
void mbedtls_sha512_clone( mbedtls_sha512_context *dst,
diff --git a/thirdparty/mbedtls/library/ssl_ciphersuites.c b/thirdparty/mbedtls/library/ssl_ciphersuites.c
index 95e6163ccc..2e9a0fd792 100644
--- a/thirdparty/mbedtls/library/ssl_ciphersuites.c
+++ b/thirdparty/mbedtls/library/ssl_ciphersuites.c
@@ -47,7 +47,7 @@
* 1. By key exchange:
* Forward-secure non-PSK > forward-secure PSK > ECJPAKE > other non-PSK > other PSK
* 2. By key length and cipher:
- * AES-256 > Camellia-256 > AES-128 > Camellia-128 > 3DES
+ * AES-256 > Camellia-256 > ARIA-256 > AES-128 > Camellia-128 > ARIA-128 > 3DES
* 3. By cipher mode when relevant GCM > CCM > CBC > CCM_8
* 4. By hash function used when relevant
* 5. By key exchange/auth again: EC > non-EC
@@ -81,6 +81,14 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
+ /* All ARIA-256 ephemeral suites */
+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
+
/* All AES-128 ephemeral suites */
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
@@ -105,6 +113,14 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
+ /* All ARIA-128 ephemeral suites */
+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
+ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
+ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
+
/* All remaining >= 128-bit ephemeral suites */
MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
@@ -121,6 +137,9 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
@@ -132,6 +151,9 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
+ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
@@ -161,6 +183,14 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384,
MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
+ /* All ARIA-256 suites */
+ MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
+ MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
+ MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
+
/* All AES-128 suites */
MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
@@ -183,6 +213,14 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256,
MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
+ /* All ARIA-128 suites */
+ MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
+ MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
+ MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
+
/* All remaining >= 128-bit suites */
MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
@@ -194,12 +232,16 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384,
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256,
MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
@@ -211,6 +253,8 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384,
MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
+ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
+ MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
@@ -219,6 +263,8 @@ static const int ciphersuite_preference[] =
MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256,
MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
+ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
+ MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA,
@@ -1688,6 +1734,365 @@ static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] =
#endif /* MBEDTLS_DES_C */
#endif /* MBEDTLS_ENABLE_WEAK_CIPHERSUITES */
+#if defined(MBEDTLS_ARIA_C)
+
+#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-RSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-RSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-RSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-RSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
+ "TLS-RSA-PSK-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
+ "TLS-RSA-PSK-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
+ "TLS-RSA-PSK-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
+ "TLS-RSA-PSK-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
+ "TLS-PSK-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384,MBEDTLS_KEY_EXCHANGE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
+ "TLS-PSK-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
+ "TLS-PSK-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
+ "TLS-PSK-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-ECDH-RSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-ECDH-RSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-ECDH-RSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-ECDH-RSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-ECDHE-RSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED)
+
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
+ "TLS-ECDHE-PSK-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
+ "TLS-ECDHE-PSK-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-ECDH-ECDSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-ECDH-ECDSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-ECDH-ECDSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-ECDH-ECDSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
+ "TLS-DHE-RSA-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
+ "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
+ "TLS-DHE-RSA-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
+ "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED)
+
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
+ "TLS-DHE-PSK-WITH-ARIA-256-GCM-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA512_C))
+ { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
+ "TLS-DHE-PSK-WITH-ARIA-256-CBC-SHA384",
+ MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
+ "TLS-DHE-PSK-WITH-ARIA-128-GCM-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+#if (defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_SHA256_C))
+ { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
+ "TLS-DHE-PSK-WITH-ARIA-128-CBC-SHA256",
+ MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+ 0 },
+#endif
+
+#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */
+
+#endif /* MBEDTLS_ARIA_C */
+
+
{ 0, "",
MBEDTLS_CIPHER_NONE, MBEDTLS_MD_NONE, MBEDTLS_KEY_EXCHANGE_NONE,
0, 0, 0, 0, 0 }
diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c
index 88864b8136..7455e99d2e 100644
--- a/thirdparty/mbedtls/library/ssl_cli.c
+++ b/thirdparty/mbedtls/library/ssl_cli.c
@@ -48,10 +48,7 @@
#endif
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
+#include "mbedtls/platform_util.h"
#endif
#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
@@ -355,7 +352,7 @@ static void ssl_write_supported_point_formats_ext( mbedtls_ssl_context *ssl,
*olen = 6;
}
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
@@ -717,6 +714,49 @@ static int ssl_generate_random( mbedtls_ssl_context *ssl )
return( 0 );
}
+/**
+ * \brief Validate cipher suite against config in SSL context.
+ *
+ * \param suite_info cipher suite to validate
+ * \param ssl SSL context
+ * \param min_minor_ver Minimal minor version to accept a cipher suite
+ * \param max_minor_ver Maximal minor version to accept a cipher suite
+ *
+ * \return 0 if valid, else 1
+ */
+static int ssl_validate_ciphersuite( const mbedtls_ssl_ciphersuite_t * suite_info,
+ const mbedtls_ssl_context * ssl,
+ int min_minor_ver, int max_minor_ver )
+{
+ (void) ssl;
+ if( suite_info == NULL )
+ return( 1 );
+
+ if( suite_info->min_minor_ver > max_minor_ver ||
+ suite_info->max_minor_ver < min_minor_ver )
+ return( 1 );
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+ if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+ ( suite_info->flags & MBEDTLS_CIPHERSUITE_NODTLS ) )
+ return( 1 );
+#endif
+
+#if defined(MBEDTLS_ARC4_C)
+ if( ssl->conf->arc4_disabled == MBEDTLS_SSL_ARC4_DISABLED &&
+ suite_info->cipher == MBEDTLS_CIPHER_ARC4_128 )
+ return( 1 );
+#endif
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+ if( suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE &&
+ mbedtls_ecjpake_check( &ssl->handshake->ecjpake_ctx ) != 0 )
+ return( 1 );
+#endif
+
+ return( 0 );
+}
+
static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
{
int ret;
@@ -869,31 +909,11 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
{
ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( ciphersuites[i] );
- if( ciphersuite_info == NULL )
+ if( ssl_validate_ciphersuite( ciphersuite_info, ssl,
+ ssl->conf->min_minor_ver,
+ ssl->conf->max_minor_ver ) != 0 )
continue;
- if( ciphersuite_info->min_minor_ver > ssl->conf->max_minor_ver ||
- ciphersuite_info->max_minor_ver < ssl->conf->min_minor_ver )
- continue;
-
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
- if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
- ( ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_NODTLS ) )
- continue;
-#endif
-
-#if defined(MBEDTLS_ARC4_C)
- if( ssl->conf->arc4_disabled == MBEDTLS_SSL_ARC4_DISABLED &&
- ciphersuite_info->cipher == MBEDTLS_CIPHER_ARC4_128 )
- continue;
-#endif
-
-#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE &&
- mbedtls_ecjpake_check( &ssl->handshake->ecjpake_ctx ) != 0 )
- continue;
-#endif
-
MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, add ciphersuite: %04x",
ciphersuites[i] ) );
@@ -938,7 +958,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
#endif
/*
- * We don't support compression with DTLS right now: is many records come
+ * We don't support compression with DTLS right now: if many records come
* in the same datagram, uncompressing one could overwrite the next one.
* We don't want to add complexity for handling that case unless there is
* an actual need for it.
@@ -1261,7 +1281,7 @@ static int ssl_parse_supported_point_formats_ext( mbedtls_ssl_context *ssl,
MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE );
return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO );
}
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
@@ -1690,22 +1710,9 @@ static int ssl_parse_server_hello( mbedtls_ssl_context *ssl )
MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %04x", i ) );
MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", buf[37 + n] ) );
- suite_info = mbedtls_ssl_ciphersuite_from_id( ssl->session_negotiate->ciphersuite );
- if( suite_info == NULL
-#if defined(MBEDTLS_ARC4_C)
- || ( ssl->conf->arc4_disabled &&
- suite_info->cipher == MBEDTLS_CIPHER_ARC4_128 )
-#endif
- )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server hello message" ) );
- mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER );
- return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO );
- }
-
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %s", suite_info->name ) );
-
+ /*
+ * Perform cipher suite validation in same way as in ssl_write_client_hello.
+ */
i = 0;
while( 1 )
{
@@ -1724,6 +1731,17 @@ static int ssl_parse_server_hello( mbedtls_ssl_context *ssl )
}
}
+ suite_info = mbedtls_ssl_ciphersuite_from_id( ssl->session_negotiate->ciphersuite );
+ if( ssl_validate_ciphersuite( suite_info, ssl, ssl->minor_ver, ssl->minor_ver ) != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server hello message" ) );
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER );
+ return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO );
+ }
+
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %s", suite_info->name ) );
+
if( comp != MBEDTLS_SSL_COMPRESS_NULL
#if defined(MBEDTLS_ZLIB_SUPPORT)
&& comp != MBEDTLS_SSL_COMPRESS_DEFLATE
@@ -2673,10 +2691,27 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl )
buf = ssl->in_msg;
/* certificate_types */
+ if( ssl->in_hslen <= mbedtls_ssl_hs_hdr_len( ssl ) )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR );
+ return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
cert_type_len = buf[mbedtls_ssl_hs_hdr_len( ssl )];
n = cert_type_len;
- if( ssl->in_hslen < mbedtls_ssl_hs_hdr_len( ssl ) + 2 + n )
+ /*
+ * In the subsequent code there are two paths that read from buf:
+ * * the length of the signature algorithms field (if minor version of
+ * SSL is 3),
+ * * distinguished name length otherwise.
+ * Both reach at most the index:
+ * ...hdr_len + 2 + n,
+ * therefore the buffer length at this point must be greater than that
+ * regardless of the actual code path.
+ */
+ if( ssl->in_hslen <= mbedtls_ssl_hs_hdr_len( ssl ) + 2 + n )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
@@ -2691,9 +2726,32 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl )
size_t sig_alg_len = ( ( buf[mbedtls_ssl_hs_hdr_len( ssl ) + 1 + n] << 8 )
| ( buf[mbedtls_ssl_hs_hdr_len( ssl ) + 2 + n] ) );
#if defined(MBEDTLS_DEBUG_C)
- unsigned char* sig_alg = buf + mbedtls_ssl_hs_hdr_len( ssl ) + 3 + n;
+ unsigned char* sig_alg;
size_t i;
+#endif
+
+ /*
+ * The furthest access in buf is in the loop few lines below:
+ * sig_alg[i + 1],
+ * where:
+ * sig_alg = buf + ...hdr_len + 3 + n,
+ * max(i) = sig_alg_len - 1.
+ * Therefore the furthest access is:
+ * buf[...hdr_len + 3 + n + sig_alg_len - 1 + 1],
+ * which reduces to:
+ * buf[...hdr_len + 3 + n + sig_alg_len],
+ * which is one less than we need the buf to be.
+ */
+ if( ssl->in_hslen <= mbedtls_ssl_hs_hdr_len( ssl ) + 3 + n + sig_alg_len )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR );
+ return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
+#if defined(MBEDTLS_DEBUG_C)
+ sig_alg = buf + mbedtls_ssl_hs_hdr_len( ssl ) + 3 + n;
for( i = 0; i < sig_alg_len; i += 2 )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "Supported Signature Algorithm found: %d"
@@ -2702,14 +2760,6 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl )
#endif
n += 2 + sig_alg_len;
-
- if( ssl->in_hslen < mbedtls_ssl_hs_hdr_len( ssl ) + 2 + n )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
- mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR );
- return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
- }
}
#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
@@ -3289,8 +3339,8 @@ static int ssl_parse_new_session_ticket( mbedtls_ssl_context *ssl )
if( ticket_len == 0 )
return( 0 );
- mbedtls_zeroize( ssl->session_negotiate->ticket,
- ssl->session_negotiate->ticket_len );
+ mbedtls_platform_zeroize( ssl->session_negotiate->ticket,
+ ssl->session_negotiate->ticket_len );
mbedtls_free( ssl->session_negotiate->ticket );
ssl->session_negotiate->ticket = NULL;
ssl->session_negotiate->ticket_len = 0;
diff --git a/thirdparty/mbedtls/library/ssl_cookie.c b/thirdparty/mbedtls/library/ssl_cookie.c
index caf119990d..56e9bdd2bf 100644
--- a/thirdparty/mbedtls/library/ssl_cookie.c
+++ b/thirdparty/mbedtls/library/ssl_cookie.c
@@ -40,14 +40,10 @@
#include "mbedtls/ssl_cookie.h"
#include "mbedtls/ssl_internal.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* If DTLS is in use, then at least one of SHA-1, SHA-256, SHA-512 is
* available. Try SHA-256 first, 512 wastes resources since we need to stay
@@ -101,7 +97,7 @@ void mbedtls_ssl_cookie_free( mbedtls_ssl_cookie_ctx *ctx )
mbedtls_mutex_free( &ctx->mutex );
#endif
- mbedtls_zeroize( ctx, sizeof( mbedtls_ssl_cookie_ctx ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ssl_cookie_ctx ) );
}
int mbedtls_ssl_cookie_setup( mbedtls_ssl_cookie_ctx *ctx,
@@ -122,7 +118,7 @@ int mbedtls_ssl_cookie_setup( mbedtls_ssl_cookie_ctx *ctx,
if( ret != 0 )
return( ret );
- mbedtls_zeroize( key, sizeof( key ) );
+ mbedtls_platform_zeroize( key, sizeof( key ) );
return( 0 );
}
diff --git a/thirdparty/mbedtls/library/ssl_srv.c b/thirdparty/mbedtls/library/ssl_srv.c
index aca4235e6e..09b7a3fed3 100644
--- a/thirdparty/mbedtls/library/ssl_srv.c
+++ b/thirdparty/mbedtls/library/ssl_srv.c
@@ -38,6 +38,7 @@
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ssl_internal.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -49,13 +50,6 @@
#include "mbedtls/platform_time.h"
#endif
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-#endif
-
#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY)
int mbedtls_ssl_set_client_transport_id( mbedtls_ssl_context *ssl,
const unsigned char *info,
@@ -553,7 +547,7 @@ static int ssl_parse_session_ticket_ext( mbedtls_ssl_context *ssl,
memcpy( ssl->session_negotiate, &session, sizeof( mbedtls_ssl_session ) );
/* Zeroize instead of free as we copied the content */
- mbedtls_zeroize( &session, sizeof( mbedtls_ssl_session ) );
+ mbedtls_platform_zeroize( &session, sizeof( mbedtls_ssl_session ) );
MBEDTLS_SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) );
@@ -793,7 +787,7 @@ static int ssl_ciphersuite_match( mbedtls_ssl_context *ssl, int suite_id,
const mbedtls_ssl_ciphersuite_t *suite_info;
#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
- defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
+ defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
mbedtls_pk_type_t sig_type;
#endif
@@ -2961,7 +2955,7 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
return( ret );
}
-#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED)
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED)
dig_signed = p;
dig_signed_len = len;
#endif
@@ -3050,7 +3044,7 @@ curve_matching_done:
/*
* 3.1: Choose hash algorithm:
- * A: For TLS 1.2, obey signature-hash-algorithm extension
+ * A: For TLS 1.2, obey signature-hash-algorithm extension
* to choose appropriate hash.
* B: For SSL3, TLS1.0, TLS1.1 and ECDHE_ECDSA, use SHA1
* (RFC 4492, Sec. 5.4)
@@ -3071,7 +3065,7 @@ curve_matching_done:
sig_alg ) ) == MBEDTLS_MD_NONE )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
- /* (... because we choose a cipher suite
+ /* (... because we choose a cipher suite
* only if there is a matching hash.) */
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
@@ -3750,7 +3744,10 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
/* Read the message without adding it to the checksum */
do {
- if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 )
+ do ret = mbedtls_ssl_read_record_layer( ssl );
+ while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
+
+ if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret );
@@ -3758,7 +3755,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
ret = mbedtls_ssl_handle_message_type( ssl );
- } while( MBEDTLS_ERR_SSL_NON_FATAL == ret );
+ } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
+ MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret )
{
diff --git a/thirdparty/mbedtls/library/ssl_ticket.c b/thirdparty/mbedtls/library/ssl_ticket.c
index 4d9116d214..a2b304869e 100644
--- a/thirdparty/mbedtls/library/ssl_ticket.c
+++ b/thirdparty/mbedtls/library/ssl_ticket.c
@@ -36,14 +36,10 @@
#endif
#include "mbedtls/ssl_ticket.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Initialze context
*/
@@ -83,7 +79,7 @@ static int ssl_ticket_gen_key( mbedtls_ssl_ticket_context *ctx,
mbedtls_cipher_get_key_bitlen( &key->ctx ),
MBEDTLS_ENCRYPT );
- mbedtls_zeroize( buf, sizeof( buf ) );
+ mbedtls_platform_zeroize( buf, sizeof( buf ) );
return( ret );
}
@@ -483,7 +479,7 @@ void mbedtls_ssl_ticket_free( mbedtls_ssl_ticket_context *ctx )
mbedtls_mutex_free( &ctx->mutex );
#endif
- mbedtls_zeroize( ctx, sizeof( mbedtls_ssl_ticket_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ssl_ticket_context ) );
}
#endif /* MBEDTLS_SSL_TICKET_C */
diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c
index 236e52d767..e8e0cd854b 100644
--- a/thirdparty/mbedtls/library/ssl_tls.c
+++ b/thirdparty/mbedtls/library/ssl_tls.c
@@ -46,6 +46,7 @@
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ssl_internal.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -53,11 +54,6 @@
#include "mbedtls/oid.h"
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/* Length of the "epoch" field in the record header */
static inline size_t ssl_ep_len( const mbedtls_ssl_context *ssl )
{
@@ -269,8 +265,8 @@ exit:
mbedtls_md5_free( &md5 );
mbedtls_sha1_free( &sha1 );
- mbedtls_zeroize( padding, sizeof( padding ) );
- mbedtls_zeroize( sha1sum, sizeof( sha1sum ) );
+ mbedtls_platform_zeroize( padding, sizeof( padding ) );
+ mbedtls_platform_zeroize( sha1sum, sizeof( sha1sum ) );
return( ret );
}
@@ -367,8 +363,8 @@ static int tls1_prf( const unsigned char *secret, size_t slen,
mbedtls_md_free( &md_ctx );
- mbedtls_zeroize( tmp, sizeof( tmp ) );
- mbedtls_zeroize( h_i, sizeof( h_i ) );
+ mbedtls_platform_zeroize( tmp, sizeof( tmp ) );
+ mbedtls_platform_zeroize( h_i, sizeof( h_i ) );
return( 0 );
}
@@ -432,8 +428,8 @@ static int tls_prf_generic( mbedtls_md_type_t md_type,
mbedtls_md_free( &md_ctx );
- mbedtls_zeroize( tmp, sizeof( tmp ) );
- mbedtls_zeroize( h_i, sizeof( h_i ) );
+ mbedtls_platform_zeroize( tmp, sizeof( tmp ) );
+ mbedtls_platform_zeroize( h_i, sizeof( h_i ) );
return( 0 );
}
@@ -642,7 +638,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
return( ret );
}
- mbedtls_zeroize( handshake->premaster, sizeof(handshake->premaster) );
+ mbedtls_platform_zeroize( handshake->premaster,
+ sizeof(handshake->premaster) );
}
else
MBEDTLS_SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) );
@@ -653,7 +650,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
memcpy( tmp, handshake->randbytes, 64 );
memcpy( handshake->randbytes, tmp + 32, 32 );
memcpy( handshake->randbytes + 32, tmp, 32 );
- mbedtls_zeroize( tmp, sizeof( tmp ) );
+ mbedtls_platform_zeroize( tmp, sizeof( tmp ) );
/*
* SSLv3:
@@ -681,7 +678,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
MBEDTLS_SSL_DEBUG_BUF( 4, "random bytes", handshake->randbytes, 64 );
MBEDTLS_SSL_DEBUG_BUF( 4, "key block", keyblk, 256 );
- mbedtls_zeroize( handshake->randbytes, sizeof( handshake->randbytes ) );
+ mbedtls_platform_zeroize( handshake->randbytes,
+ sizeof( handshake->randbytes ) );
/*
* Determine the appropriate key, IV and MAC length.
@@ -855,8 +853,13 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
defined(MBEDTLS_SSL_PROTO_TLS1_2)
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 )
{
- mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, mac_key_len );
- mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, mac_key_len );
+ /* For HMAC-based ciphersuites, initialize the HMAC transforms.
+ For AEAD-based ciphersuites, there is nothing to do here. */
+ if( mac_key_len != 0 )
+ {
+ mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, mac_key_len );
+ mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, mac_key_len );
+ }
}
else
#endif
@@ -943,7 +946,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
}
#endif /* MBEDTLS_CIPHER_MODE_CBC */
- mbedtls_zeroize( keyblk, sizeof( keyblk ) );
+ mbedtls_platform_zeroize( keyblk, sizeof( keyblk ) );
#if defined(MBEDTLS_ZLIB_SUPPORT)
// Initialize compression
@@ -1269,7 +1272,7 @@ static void ssl_mac( mbedtls_md_context_t *md_ctx,
#if defined(MBEDTLS_ARC4_C) || defined(MBEDTLS_CIPHER_NULL_CIPHER) || \
( defined(MBEDTLS_CIPHER_MODE_CBC) && \
- ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) ) )
+ ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) || defined(MBEDTLS_ARIA_C)) )
#define SSL_SOME_MODES_USE_MAC
#endif
@@ -1470,7 +1473,7 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
else
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */
#if defined(MBEDTLS_CIPHER_MODE_CBC) && \
- ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) )
+ ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) || defined(MBEDTLS_ARIA_C) )
if( mode == MBEDTLS_MODE_CBC )
{
int ret;
@@ -1586,7 +1589,7 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
}
else
#endif /* MBEDTLS_CIPHER_MODE_CBC &&
- ( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C ) */
+ ( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C || MBEDTLS_ARIA_C ) */
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@@ -1730,7 +1733,7 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl )
else
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */
#if defined(MBEDTLS_CIPHER_MODE_CBC) && \
- ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) )
+ ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) || defined(MBEDTLS_ARIA_C) )
if( mode == MBEDTLS_MODE_CBC )
{
/*
@@ -1942,7 +1945,7 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl )
}
else
#endif /* MBEDTLS_CIPHER_MODE_CBC &&
- ( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C ) */
+ ( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C || MBEDTLS_ARIA_C ) */
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@@ -2103,6 +2106,7 @@ static int ssl_compress_buf( mbedtls_ssl_context *ssl )
{
int ret;
unsigned char *msg_post = ssl->out_msg;
+ ptrdiff_t bytes_written = ssl->out_msg - ssl->out_buf;
size_t len_pre = ssl->out_msglen;
unsigned char *msg_pre = ssl->compress_buf;
@@ -2122,7 +2126,7 @@ static int ssl_compress_buf( mbedtls_ssl_context *ssl )
ssl->transform_out->ctx_deflate.next_in = msg_pre;
ssl->transform_out->ctx_deflate.avail_in = len_pre;
ssl->transform_out->ctx_deflate.next_out = msg_post;
- ssl->transform_out->ctx_deflate.avail_out = MBEDTLS_SSL_BUFFER_LEN;
+ ssl->transform_out->ctx_deflate.avail_out = MBEDTLS_SSL_BUFFER_LEN - bytes_written;
ret = deflate( &ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH );
if( ret != Z_OK )
@@ -2132,7 +2136,7 @@ static int ssl_compress_buf( mbedtls_ssl_context *ssl )
}
ssl->out_msglen = MBEDTLS_SSL_BUFFER_LEN -
- ssl->transform_out->ctx_deflate.avail_out;
+ ssl->transform_out->ctx_deflate.avail_out - bytes_written;
MBEDTLS_SSL_DEBUG_MSG( 3, ( "after compression: msglen = %d, ",
ssl->out_msglen ) );
@@ -2149,6 +2153,7 @@ static int ssl_decompress_buf( mbedtls_ssl_context *ssl )
{
int ret;
unsigned char *msg_post = ssl->in_msg;
+ ptrdiff_t header_bytes = ssl->in_msg - ssl->in_buf;
size_t len_pre = ssl->in_msglen;
unsigned char *msg_pre = ssl->compress_buf;
@@ -2168,7 +2173,8 @@ static int ssl_decompress_buf( mbedtls_ssl_context *ssl )
ssl->transform_in->ctx_inflate.next_in = msg_pre;
ssl->transform_in->ctx_inflate.avail_in = len_pre;
ssl->transform_in->ctx_inflate.next_out = msg_post;
- ssl->transform_in->ctx_inflate.avail_out = MBEDTLS_SSL_MAX_CONTENT_LEN;
+ ssl->transform_in->ctx_inflate.avail_out = MBEDTLS_SSL_BUFFER_LEN -
+ header_bytes;
ret = inflate( &ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH );
if( ret != Z_OK )
@@ -2177,8 +2183,8 @@ static int ssl_decompress_buf( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_COMPRESSION_FAILED );
}
- ssl->in_msglen = MBEDTLS_SSL_MAX_CONTENT_LEN -
- ssl->transform_in->ctx_inflate.avail_out;
+ ssl->in_msglen = MBEDTLS_SSL_BUFFER_LEN -
+ ssl->transform_in->ctx_inflate.avail_out - header_bytes;
MBEDTLS_SSL_DEBUG_MSG( 3, ( "after decompression: msglen = %d, ",
ssl->in_msglen ) );
@@ -2332,7 +2338,10 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want )
* that will end up being dropped.
*/
if( ssl_check_timer( ssl ) != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "timer has expired" ) );
ret = MBEDTLS_ERR_SSL_TIMEOUT;
+ }
else
{
len = MBEDTLS_SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf );
@@ -2434,6 +2443,14 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want )
if( ret < 0 )
return( ret );
+ if ( (size_t)ret > len || ( INT_MAX > SIZE_MAX && ret > SIZE_MAX ) )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1,
+ ( "f_recv returned %d bytes but only %lu were requested",
+ ret, (unsigned long)len ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ }
+
ssl->in_left += ret;
}
}
@@ -2481,6 +2498,14 @@ int mbedtls_ssl_flush_output( mbedtls_ssl_context *ssl )
if( ret <= 0 )
return( ret );
+ if( (size_t)ret > ssl->out_left || ( INT_MAX > SIZE_MAX && ret > SIZE_MAX ) )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1,
+ ( "f_send returned %d bytes but only %lu bytes were sent",
+ ret, (unsigned long)ssl->out_left ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ }
+
ssl->out_left -= ret;
}
@@ -3064,7 +3089,7 @@ static int ssl_reassemble_dtls_handshake( mbedtls_ssl_context *ssl )
if( ssl_bitmask_check( bitmask, msg_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "message is not complete yet" ) );
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake message completed" ) );
@@ -3141,9 +3166,11 @@ int mbedtls_ssl_prepare_handshake_record( mbedtls_ssl_context *ssl )
int ret;
unsigned int recv_msg_seq = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5];
- /* ssl->handshake is NULL when receiving ClientHello for renego */
if( ssl->handshake != NULL &&
- recv_msg_seq != ssl->handshake->in_msg_seq )
+ ( ( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER &&
+ recv_msg_seq != ssl->handshake->in_msg_seq ) ||
+ ( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER &&
+ ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_HELLO ) ) )
{
/* Retransmit only on last message from previous flight, to avoid
* too many retransmissions.
@@ -3170,7 +3197,7 @@ int mbedtls_ssl_prepare_handshake_record( mbedtls_ssl_context *ssl )
ssl->handshake->in_msg_seq ) );
}
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
/* Wait until message completion to increment in_msg_seq */
@@ -3573,81 +3600,23 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
- /* Check length against bounds of the current transform and version */
- if( ssl->transform_in == NULL )
- {
- if( ssl->in_msglen < 1 ||
- ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
- }
- else
- {
- if( ssl->in_msglen < ssl->transform_in->minlen )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-
-#if defined(MBEDTLS_SSL_PROTO_SSL3)
- if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
- ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-#endif
-#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
- defined(MBEDTLS_SSL_PROTO_TLS1_2)
- /*
- * TLS encrypted messages can have up to 256 bytes of padding
- */
- if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
- ssl->in_msglen > ssl->transform_in->minlen +
- MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-#endif
- }
-
/*
- * DTLS-related tests done last, because most of them may result in
- * silently dropping the record (but not the whole datagram), and we only
- * want to consider that after ensuring that the "basic" fields (type,
- * version, length) are sane.
+ * DTLS-related tests.
+ * Check epoch before checking length constraint because
+ * the latter varies with the epoch. E.g., if a ChangeCipherSpec
+ * message gets duplicated before the corresponding Finished message,
+ * the second ChangeCipherSpec should be discarded because it belongs
+ * to an old epoch, but not because its length is shorter than
+ * the minimum record length for packets using the new record transform.
+ * Note that these two kinds of failures are handled differently,
+ * as an unexpected record is silently skipped but an invalid
+ * record leads to the entire datagram being dropped.
*/
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{
unsigned int rec_epoch = ( ssl->in_ctr[0] << 8 ) | ssl->in_ctr[1];
- /* Drop unexpected ChangeCipherSpec messages */
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
- ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
- ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
- }
-
- /* Drop unexpected ApplicationData records,
- * except at the beginning of renegotiations */
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
- ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
- && ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
- ssl->state == MBEDTLS_SSL_SERVER_HELLO )
-#endif
- )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
- }
-
/* Check epoch (and sequence number) with DTLS */
if( rec_epoch != ssl->in_epoch )
{
@@ -3687,9 +3656,74 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
#endif
+
+ /* Drop unexpected ChangeCipherSpec messages */
+ if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
+ ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
+ ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
+ return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
+ }
+
+ /* Drop unexpected ApplicationData records,
+ * except at the beginning of renegotiations */
+ if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
+ ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+ && ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
+ ssl->state == MBEDTLS_SSL_SERVER_HELLO )
+#endif
+ )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
+ return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
+ }
}
#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+ /* Check length against bounds of the current transform and version */
+ if( ssl->transform_in == NULL )
+ {
+ if( ssl->in_msglen < 1 ||
+ ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+ }
+ else
+ {
+ if( ssl->in_msglen < ssl->transform_in->minlen )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+
+#if defined(MBEDTLS_SSL_PROTO_SSL3)
+ if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
+ ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+#endif
+#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
+ defined(MBEDTLS_SSL_PROTO_TLS1_2)
+ /*
+ * TLS encrypted messages can have up to 256 bytes of padding
+ */
+ if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
+ ssl->in_msglen > ssl->transform_in->minlen +
+ MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+#endif
+ }
+
return( 0 );
}
@@ -3778,7 +3812,10 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl )
{
do {
- if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 )
+ do ret = mbedtls_ssl_read_record_layer( ssl );
+ while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
+
+ if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret );
@@ -3786,11 +3823,12 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl )
ret = mbedtls_ssl_handle_message_type( ssl );
- } while( MBEDTLS_ERR_SSL_NON_FATAL == ret );
+ } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
+ MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret )
{
- MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
+ MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_handle_message_type" ), ret );
return( ret );
}
@@ -3828,11 +3866,6 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
* (2) Alert messages:
* Consume whole record content, in_msglen = 0.
*
- * NOTE: This needs to be fixed, since like for
- * handshake messages it is allowed to have
- * multiple alerts witin a single record.
- * Internal reference IOTSSL-1321.
- *
* (3) Change cipher spec:
* Consume whole record content, in_msglen = 0.
*
@@ -3860,12 +3893,12 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
*/
/* Notes:
- * (1) in_hslen is *NOT* necessarily the size of the
+ * (1) in_hslen is not necessarily the size of the
* current handshake content: If DTLS handshake
* fragmentation is used, that's the fragment
* size instead. Using the total handshake message
- * size here is FAULTY and should be changed at
- * some point. Internal reference IOTSSL-1414.
+ * size here is faulty and should be changed at
+ * some point.
* (2) While it doesn't seem to cause problems, one
* has to be very careful not to assume that in_hslen
* is always <= in_msglen in a sensible communication.
@@ -3916,12 +3949,6 @@ int mbedtls_ssl_read_record_layer( mbedtls_ssl_context *ssl )
return( 0 );
}
- /* Need to fetch a new record */
-
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
-read_record_header:
-#endif
-
/* Current record either fully processed or to be discarded. */
if( ( ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_hdr_len( ssl ) ) ) != 0 )
@@ -3956,7 +3983,7 @@ read_record_header:
}
/* Get next record */
- goto read_record_header;
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
#endif
return( ret );
@@ -3975,7 +4002,13 @@ read_record_header:
/* Done reading this record, get ready for the next one */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+ {
ssl->next_record_offset = ssl->in_msglen + mbedtls_ssl_hdr_len( ssl );
+ if( ssl->next_record_offset < ssl->in_left )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "more than one record within datagram" ) );
+ }
+ }
else
#endif
ssl->in_left = 0;
@@ -4022,7 +4055,7 @@ read_record_header:
ssl->in_left = 0;
MBEDTLS_SSL_DEBUG_MSG( 1, ( "discarding invalid record (mac)" ) );
- goto read_record_header;
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
return( ret );
@@ -4043,46 +4076,6 @@ read_record_header:
}
}
- /*
- * When we sent the last flight of the handshake, we MUST respond to a
- * retransmit of the peer's previous flight with a retransmit. (In
- * practice, only the Finished message will make it, other messages
- * including CCS use the old transform so they're dropped as invalid.)
- *
- * If the record we received is not a handshake message, however, it
- * means the peer received our last flight so we can clean up
- * handshake info.
- *
- * This check needs to be done before prepare_handshake() due to an edge
- * case: if the client immediately requests renegotiation, this
- * finishes the current handshake first, avoiding the new ClientHello
- * being mistaken for an ancient message in the current handshake.
- */
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
- if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
- ssl->handshake != NULL &&
- ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
- {
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
- ssl->in_msg[0] == MBEDTLS_SSL_HS_FINISHED )
- {
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "received retransmit of last flight" ) );
-
- if( ( ret = mbedtls_ssl_resend( ssl ) ) != 0 )
- {
- MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_resend", ret );
- return( ret );
- }
-
- return( MBEDTLS_ERR_SSL_WANT_READ );
- }
- else
- {
- ssl_handshake_wrapup_free_hs_transform( ssl );
- }
- }
-#endif
-
return( 0 );
}
@@ -4127,7 +4120,7 @@ int mbedtls_ssl_handle_message_type( mbedtls_ssl_context *ssl )
if( ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING &&
ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION )
{
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no_cert" ) );
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no renegotiation alert" ) );
/* Will be handled when trying to parse ServerHello */
return( 0 );
}
@@ -4149,6 +4142,15 @@ int mbedtls_ssl_handle_message_type( mbedtls_ssl_context *ssl )
return MBEDTLS_ERR_SSL_NON_FATAL;
}
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+ if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
+ {
+ ssl_handshake_wrapup_free_hs_transform( ssl );
+ }
+#endif
+
return( 0 );
}
@@ -5026,9 +5028,9 @@ static void ssl_calc_finished_ssl(
mbedtls_md5_free( &md5 );
mbedtls_sha1_free( &sha1 );
- mbedtls_zeroize( padbuf, sizeof( padbuf ) );
- mbedtls_zeroize( md5sum, sizeof( md5sum ) );
- mbedtls_zeroize( sha1sum, sizeof( sha1sum ) );
+ mbedtls_platform_zeroize( padbuf, sizeof( padbuf ) );
+ mbedtls_platform_zeroize( md5sum, sizeof( md5sum ) );
+ mbedtls_platform_zeroize( sha1sum, sizeof( sha1sum ) );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= calc finished" ) );
}
@@ -5087,7 +5089,7 @@ static void ssl_calc_finished_tls(
mbedtls_md5_free( &md5 );
mbedtls_sha1_free( &sha1 );
- mbedtls_zeroize( padbuf, sizeof( padbuf ) );
+ mbedtls_platform_zeroize( padbuf, sizeof( padbuf ) );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= calc finished" ) );
}
@@ -5137,7 +5139,7 @@ static void ssl_calc_finished_tls_sha256(
mbedtls_sha256_free( &sha256 );
- mbedtls_zeroize( padbuf, sizeof( padbuf ) );
+ mbedtls_platform_zeroize( padbuf, sizeof( padbuf ) );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= calc finished" ) );
}
@@ -5186,7 +5188,7 @@ static void ssl_calc_finished_tls_sha384(
mbedtls_sha512_free( &sha512 );
- mbedtls_zeroize( padbuf, sizeof( padbuf ) );
+ mbedtls_platform_zeroize( padbuf, sizeof( padbuf ) );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= calc finished" ) );
}
@@ -6105,7 +6107,7 @@ int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf,
if( conf->psk != NULL )
{
- mbedtls_zeroize( conf->psk, conf->psk_len );
+ mbedtls_platform_zeroize( conf->psk, conf->psk_len );
mbedtls_free( conf->psk );
conf->psk = NULL;
@@ -6148,7 +6150,8 @@ int mbedtls_ssl_set_hs_psk( mbedtls_ssl_context *ssl,
if( ssl->handshake->psk != NULL )
{
- mbedtls_zeroize( ssl->handshake->psk, ssl->handshake->psk_len );
+ mbedtls_platform_zeroize( ssl->handshake->psk,
+ ssl->handshake->psk_len );
mbedtls_free( ssl->handshake->psk );
ssl->handshake->psk_len = 0;
}
@@ -6278,7 +6281,7 @@ int mbedtls_ssl_set_hostname( mbedtls_ssl_context *ssl, const char *hostname )
if( ssl->hostname != NULL )
{
- mbedtls_zeroize( ssl->hostname, strlen( ssl->hostname ) );
+ mbedtls_platform_zeroize( ssl->hostname, strlen( ssl->hostname ) );
mbedtls_free( ssl->hostname );
}
@@ -6485,6 +6488,61 @@ size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl )
return( ssl->in_offt == NULL ? 0 : ssl->in_msglen );
}
+int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl )
+{
+ /*
+ * Case A: We're currently holding back
+ * a message for further processing.
+ */
+
+ if( ssl->keep_current_message == 1 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: record held back for processing" ) );
+ return( 1 );
+ }
+
+ /*
+ * Case B: Further records are pending in the current datagram.
+ */
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+ if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+ ssl->in_left > ssl->next_record_offset )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more records within current datagram" ) );
+ return( 1 );
+ }
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+ /*
+ * Case C: A handshake message is being processed.
+ */
+
+ if( ssl->in_hslen > 0 && ssl->in_hslen < ssl->in_msglen )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more handshake messages within current record" ) );
+ return( 1 );
+ }
+
+ /*
+ * Case D: An application data message is being processed
+ */
+ if( ssl->in_offt != NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: application data record is being processed" ) );
+ return( 1 );
+ }
+
+ /*
+ * In all other cases, the rest of the message can be dropped.
+ * As in ssl_read_record_layer, this needs to be adapted if
+ * we implement support for multiple alerts in single records.
+ */
+
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: nothing pending" ) );
+ return( 0 );
+}
+
uint32_t mbedtls_ssl_get_verify_result( const mbedtls_ssl_context *ssl )
{
if( ssl->session != NULL )
@@ -6892,42 +6950,8 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
}
}
- /*
- * TODO
- *
- * The logic should be streamlined here:
- *
- * Instead of
- *
- * - Manually checking whether ssl->in_offt is NULL
- * - Fetching a new record if yes
- * - Setting ssl->in_offt if one finds an application record
- * - Resetting keep_current_message after handling the application data
- *
- * one should
- *
- * - Adapt read_record to set ssl->in_offt automatically
- * when a new application data record is processed.
- * - Always call mbedtls_ssl_read_record here.
- *
- * This way, the logic of ssl_read would be much clearer:
- *
- * (1) Always call record layer and see what kind of record is on
- * and have it ready for consumption (in particular, in_offt
- * properly set for application data records).
- * (2) If it's application data (either freshly fetched
- * or something already being partially processed),
- * serve the read request from it.
- * (3) If it's something different from application data,
- * handle it accordingly, e.g. potentially start a
- * renegotiation.
- *
- * This will also remove the need to manually reset
- * ssl->keep_current_message = 0 below.
- *
- */
-
- if( ssl->in_offt == NULL )
+ /* Loop as long as no application data record is available */
+ while( ssl->in_offt == NULL )
{
/* Start timer if not already running */
if( ssl->f_get_timer != NULL &&
@@ -6981,7 +7005,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
/* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ {
+ continue;
+ }
#endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
}
@@ -6996,7 +7022,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
/* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ {
+ continue;
+ }
#endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
}
@@ -7069,7 +7097,25 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
}
}
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ /* At this point, we don't know whether the renegotiation has been
+ * completed or not. The cases to consider are the following:
+ * 1) The renegotiation is complete. In this case, no new record
+ * has been read yet.
+ * 2) The renegotiation is incomplete because the client received
+ * an application data record while awaiting the ServerHello.
+ * 3) The renegotiation is incomplete because the client received
+ * a non-handshake, non-application data message while awaiting
+ * the ServerHello.
+ * In each of these case, looping will be the proper action:
+ * - For 1), the next iteration will read a new record and check
+ * if it's application data.
+ * - For 2), the loop condition isn't satisfied as application data
+ * is present, hence continue is the same as break
+ * - For 3), the loop condition is satisfied and read_record
+ * will re-deliver the message that was held back by the client
+ * when expecting the ServerHello.
+ */
+ continue;
}
#if defined(MBEDTLS_SSL_RENEGOTIATION)
else if( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING )
@@ -7324,7 +7370,7 @@ void mbedtls_ssl_transform_free( mbedtls_ssl_transform *transform )
mbedtls_md_free( &transform->md_ctx_enc );
mbedtls_md_free( &transform->md_ctx_dec );
- mbedtls_zeroize( transform, sizeof( mbedtls_ssl_transform ) );
+ mbedtls_platform_zeroize( transform, sizeof( mbedtls_ssl_transform ) );
}
#if defined(MBEDTLS_X509_CRT_PARSE_C)
@@ -7384,7 +7430,7 @@ void mbedtls_ssl_handshake_free( mbedtls_ssl_handshake_params *handshake )
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
if( handshake->psk != NULL )
{
- mbedtls_zeroize( handshake->psk, handshake->psk_len );
+ mbedtls_platform_zeroize( handshake->psk, handshake->psk_len );
mbedtls_free( handshake->psk );
}
#endif
@@ -7414,7 +7460,8 @@ void mbedtls_ssl_handshake_free( mbedtls_ssl_handshake_params *handshake )
ssl_flight_free( handshake->flight );
#endif
- mbedtls_zeroize( handshake, sizeof( mbedtls_ssl_handshake_params ) );
+ mbedtls_platform_zeroize( handshake,
+ sizeof( mbedtls_ssl_handshake_params ) );
}
void mbedtls_ssl_session_free( mbedtls_ssl_session *session )
@@ -7434,7 +7481,7 @@ void mbedtls_ssl_session_free( mbedtls_ssl_session *session )
mbedtls_free( session->ticket );
#endif
- mbedtls_zeroize( session, sizeof( mbedtls_ssl_session ) );
+ mbedtls_platform_zeroize( session, sizeof( mbedtls_ssl_session ) );
}
/*
@@ -7449,20 +7496,20 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl )
if( ssl->out_buf != NULL )
{
- mbedtls_zeroize( ssl->out_buf, MBEDTLS_SSL_BUFFER_LEN );
+ mbedtls_platform_zeroize( ssl->out_buf, MBEDTLS_SSL_BUFFER_LEN );
mbedtls_free( ssl->out_buf );
}
if( ssl->in_buf != NULL )
{
- mbedtls_zeroize( ssl->in_buf, MBEDTLS_SSL_BUFFER_LEN );
+ mbedtls_platform_zeroize( ssl->in_buf, MBEDTLS_SSL_BUFFER_LEN );
mbedtls_free( ssl->in_buf );
}
#if defined(MBEDTLS_ZLIB_SUPPORT)
if( ssl->compress_buf != NULL )
{
- mbedtls_zeroize( ssl->compress_buf, MBEDTLS_SSL_BUFFER_LEN );
+ mbedtls_platform_zeroize( ssl->compress_buf, MBEDTLS_SSL_BUFFER_LEN );
mbedtls_free( ssl->compress_buf );
}
#endif
@@ -7493,7 +7540,7 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_X509_CRT_PARSE_C)
if( ssl->hostname != NULL )
{
- mbedtls_zeroize( ssl->hostname, strlen( ssl->hostname ) );
+ mbedtls_platform_zeroize( ssl->hostname, strlen( ssl->hostname ) );
mbedtls_free( ssl->hostname );
}
#endif
@@ -7513,7 +7560,7 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl )
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= free" ) );
/* Actually clear after last debug message */
- mbedtls_zeroize( ssl, sizeof( mbedtls_ssl_context ) );
+ mbedtls_platform_zeroize( ssl, sizeof( mbedtls_ssl_context ) );
}
/*
@@ -7740,11 +7787,17 @@ void mbedtls_ssl_config_free( mbedtls_ssl_config *conf )
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
if( conf->psk != NULL )
{
- mbedtls_zeroize( conf->psk, conf->psk_len );
- mbedtls_zeroize( conf->psk_identity, conf->psk_identity_len );
+ mbedtls_platform_zeroize( conf->psk, conf->psk_len );
mbedtls_free( conf->psk );
- mbedtls_free( conf->psk_identity );
+ conf->psk = NULL;
conf->psk_len = 0;
+ }
+
+ if( conf->psk_identity != NULL )
+ {
+ mbedtls_platform_zeroize( conf->psk_identity, conf->psk_identity_len );
+ mbedtls_free( conf->psk_identity );
+ conf->psk_identity = NULL;
conf->psk_identity_len = 0;
}
#endif
@@ -7753,7 +7806,7 @@ void mbedtls_ssl_config_free( mbedtls_ssl_config *conf )
ssl_key_cert_free( conf->key_cert );
#endif
- mbedtls_zeroize( conf, sizeof( mbedtls_ssl_config ) );
+ mbedtls_platform_zeroize( conf, sizeof( mbedtls_ssl_config ) );
}
#if defined(MBEDTLS_PK_C) && \
diff --git a/thirdparty/mbedtls/library/threading.c b/thirdparty/mbedtls/library/threading.c
index 07586756f2..f1c37245c7 100644
--- a/thirdparty/mbedtls/library/threading.c
+++ b/thirdparty/mbedtls/library/threading.c
@@ -111,8 +111,12 @@ void mbedtls_threading_set_alt( void (*mutex_init)( mbedtls_threading_mutex_t *
mbedtls_mutex_lock = mutex_lock;
mbedtls_mutex_unlock = mutex_unlock;
+#if defined(MBEDTLS_FS_IO)
mbedtls_mutex_init( &mbedtls_threading_readdir_mutex );
+#endif
+#if defined(MBEDTLS_HAVE_TIME_DATE)
mbedtls_mutex_init( &mbedtls_threading_gmtime_mutex );
+#endif
}
/*
@@ -120,8 +124,12 @@ void mbedtls_threading_set_alt( void (*mutex_init)( mbedtls_threading_mutex_t *
*/
void mbedtls_threading_free_alt( void )
{
+#if defined(MBEDTLS_FS_IO)
mbedtls_mutex_free( &mbedtls_threading_readdir_mutex );
+#endif
+#if defined(MBEDTLS_HAVE_TIME_DATE)
mbedtls_mutex_free( &mbedtls_threading_gmtime_mutex );
+#endif
}
#endif /* MBEDTLS_THREADING_ALT */
@@ -131,7 +139,11 @@ void mbedtls_threading_free_alt( void )
#ifndef MUTEX_INIT
#define MUTEX_INIT
#endif
+#if defined(MBEDTLS_FS_IO)
mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex MUTEX_INIT;
+#endif
+#if defined(MBEDTLS_HAVE_TIME_DATE)
mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex MUTEX_INIT;
+#endif
#endif /* MBEDTLS_THREADING_C */
diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c
index 35d6d89e2b..6a30e51259 100644
--- a/thirdparty/mbedtls/library/timing.c
+++ b/thirdparty/mbedtls/library/timing.c
@@ -39,7 +39,7 @@
#if !defined(MBEDTLS_TIMING_ALT)
#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
- !defined(__APPLE__) && !defined(_WIN32)
+ !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__)
#error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h"
#endif
diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c
index da47e3d753..e8e448f6f8 100644
--- a/thirdparty/mbedtls/library/version_features.c
+++ b/thirdparty/mbedtls/library/version_features.c
@@ -90,6 +90,9 @@ static const char *features[] = {
#if defined(MBEDTLS_ARC4_ALT)
"MBEDTLS_ARC4_ALT",
#endif /* MBEDTLS_ARC4_ALT */
+#if defined(MBEDTLS_ARIA_ALT)
+ "MBEDTLS_ARIA_ALT",
+#endif /* MBEDTLS_ARIA_ALT */
#if defined(MBEDTLS_BLOWFISH_ALT)
"MBEDTLS_BLOWFISH_ALT",
#endif /* MBEDTLS_BLOWFISH_ALT */
@@ -237,6 +240,9 @@ static const char *features[] = {
#if defined(MBEDTLS_AES_ROM_TABLES)
"MBEDTLS_AES_ROM_TABLES",
#endif /* MBEDTLS_AES_ROM_TABLES */
+#if defined(MBEDTLS_AES_FEWER_TABLES)
+ "MBEDTLS_AES_FEWER_TABLES",
+#endif /* MBEDTLS_AES_FEWER_TABLES */
#if defined(MBEDTLS_CAMELLIA_SMALL_MEMORY)
"MBEDTLS_CAMELLIA_SMALL_MEMORY",
#endif /* MBEDTLS_CAMELLIA_SMALL_MEMORY */
@@ -306,6 +312,9 @@ static const char *features[] = {
#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
"MBEDTLS_ECP_DP_CURVE25519_ENABLED",
#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+ "MBEDTLS_ECP_DP_CURVE448_ENABLED",
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
#if defined(MBEDTLS_ECP_NIST_OPTIM)
"MBEDTLS_ECP_NIST_OPTIM",
#endif /* MBEDTLS_ECP_NIST_OPTIM */
@@ -525,6 +534,9 @@ static const char *features[] = {
#if defined(MBEDTLS_CAMELLIA_C)
"MBEDTLS_CAMELLIA_C",
#endif /* MBEDTLS_CAMELLIA_C */
+#if defined(MBEDTLS_ARIA_C)
+ "MBEDTLS_ARIA_C",
+#endif /* MBEDTLS_ARIA_C */
#if defined(MBEDTLS_CCM_C)
"MBEDTLS_CCM_C",
#endif /* MBEDTLS_CCM_C */
diff --git a/thirdparty/mbedtls/library/x509_crl.c b/thirdparty/mbedtls/library/x509_crl.c
index b0f39d428b..8450f87e03 100644
--- a/thirdparty/mbedtls/library/x509_crl.c
+++ b/thirdparty/mbedtls/library/x509_crl.c
@@ -39,6 +39,7 @@
#include "mbedtls/x509_crl.h"
#include "mbedtls/oid.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -66,11 +67,6 @@
#include <stdio.h>
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Version ::= INTEGER { v1(0), v2(1) }
*/
@@ -616,7 +612,7 @@ int mbedtls_x509_crl_parse_file( mbedtls_x509_crl *chain, const char *path )
ret = mbedtls_x509_crl_parse( chain, buf, n );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
@@ -737,7 +733,7 @@ void mbedtls_x509_crl_free( mbedtls_x509_crl *crl )
{
name_prv = name_cur;
name_cur = name_cur->next;
- mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
+ mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
mbedtls_free( name_prv );
}
@@ -746,13 +742,14 @@ void mbedtls_x509_crl_free( mbedtls_x509_crl *crl )
{
entry_prv = entry_cur;
entry_cur = entry_cur->next;
- mbedtls_zeroize( entry_prv, sizeof( mbedtls_x509_crl_entry ) );
+ mbedtls_platform_zeroize( entry_prv,
+ sizeof( mbedtls_x509_crl_entry ) );
mbedtls_free( entry_prv );
}
if( crl_cur->raw.p != NULL )
{
- mbedtls_zeroize( crl_cur->raw.p, crl_cur->raw.len );
+ mbedtls_platform_zeroize( crl_cur->raw.p, crl_cur->raw.len );
mbedtls_free( crl_cur->raw.p );
}
@@ -766,7 +763,7 @@ void mbedtls_x509_crl_free( mbedtls_x509_crl *crl )
crl_prv = crl_cur;
crl_cur = crl_cur->next;
- mbedtls_zeroize( crl_prv, sizeof( mbedtls_x509_crl ) );
+ mbedtls_platform_zeroize( crl_prv, sizeof( mbedtls_x509_crl ) );
if( crl_prv != crl )
mbedtls_free( crl_prv );
}
diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
index 2a5dbb8783..038eae0257 100644
--- a/thirdparty/mbedtls/library/x509_crt.c
+++ b/thirdparty/mbedtls/library/x509_crt.c
@@ -27,6 +27,8 @@
*
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+ *
+ * [SIRO] https://cabforum.org/wp-content/uploads/Chunghwatelecom201503cabforumV4.pdf
*/
#if !defined(MBEDTLS_CONFIG_FILE)
@@ -39,6 +41,7 @@
#include "mbedtls/x509_crt.h"
#include "mbedtls/oid.h"
+#include "mbedtls/platform_util.h"
#include <stdio.h>
#include <string.h>
@@ -88,10 +91,18 @@
#endif /* !_WIN32 || EFIX64 || EFI32 */
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
+/*
+ * Item in a verification chain: cert and flags for it
+ */
+typedef struct {
+ mbedtls_x509_crt *crt;
+ uint32_t flags;
+} x509_crt_verify_chain_item;
+
+/*
+ * Max size of verification chain: end-entity + intermediates + trusted root
+ */
+#define X509_MAX_VERIFY_CHAIN_SIZE ( MBEDTLS_X509_MAX_INTERMEDIATE_CA + 2 )
/*
* Default profile
@@ -160,7 +171,7 @@ const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb =
/*
* Check md_alg against profile
- * Return 0 if md_alg acceptable for this profile, -1 otherwise
+ * Return 0 if md_alg is acceptable for this profile, -1 otherwise
*/
static int x509_profile_check_md_alg( const mbedtls_x509_crt_profile *profile,
mbedtls_md_type_t md_alg )
@@ -173,7 +184,7 @@ static int x509_profile_check_md_alg( const mbedtls_x509_crt_profile *profile,
/*
* Check pk_alg against profile
- * Return 0 if pk_alg acceptable for this profile, -1 otherwise
+ * Return 0 if pk_alg is acceptable for this profile, -1 otherwise
*/
static int x509_profile_check_pk_alg( const mbedtls_x509_crt_profile *profile,
mbedtls_pk_type_t pk_alg )
@@ -186,12 +197,13 @@ static int x509_profile_check_pk_alg( const mbedtls_x509_crt_profile *profile,
/*
* Check key against profile
- * Return 0 if pk_alg acceptable for this profile, -1 otherwise
+ * Return 0 if pk is acceptable for this profile, -1 otherwise
*/
static int x509_profile_check_key( const mbedtls_x509_crt_profile *profile,
- mbedtls_pk_type_t pk_alg,
const mbedtls_pk_context *pk )
{
+ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type( pk );
+
#if defined(MBEDTLS_RSA_C)
if( pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS )
{
@@ -207,7 +219,7 @@ static int x509_profile_check_key( const mbedtls_x509_crt_profile *profile,
pk_alg == MBEDTLS_PK_ECKEY ||
pk_alg == MBEDTLS_PK_ECKEY_DH )
{
- mbedtls_ecp_group_id gid = mbedtls_pk_ec( *pk )->grp.id;
+ const mbedtls_ecp_group_id gid = mbedtls_pk_ec( *pk )->grp.id;
if( ( profile->allowed_curves & MBEDTLS_X509_ID_FLAG( gid ) ) != 0 )
return( 0 );
@@ -730,7 +742,7 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, const unsigned char *
memcpy( p, buf, crt->raw.len );
- // Direct pointers to the new buffer
+ // Direct pointers to the new buffer
p += crt->raw.len - len;
end = crt_end = p + len;
@@ -1112,7 +1124,7 @@ int mbedtls_x509_crt_parse_file( mbedtls_x509_crt *chain, const char *path )
ret = mbedtls_x509_crt_parse( chain, buf, n );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
@@ -1662,7 +1674,7 @@ int mbedtls_x509_crt_is_revoked( const mbedtls_x509_crt *crt, const mbedtls_x509
/*
* Check that the given certificate is not revoked according to the CRL.
- * Skip validation is no CRL for the given CA is present.
+ * Skip validation if no CRL for the given CA is present.
*/
static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
mbedtls_x509_crl *crl_list,
@@ -1707,18 +1719,14 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
flags |= MBEDTLS_X509_BADCRL_BAD_PK;
md_info = mbedtls_md_info_from_type( crl_list->sig_md );
- if( md_info == NULL )
+ if( mbedtls_md( md_info, crl_list->tbs.p, crl_list->tbs.len, hash ) != 0 )
{
- /*
- * Cannot check 'unknown' hash
- */
+ /* Note: this can't happen except after an internal error */
flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED;
break;
}
- mbedtls_md( md_info, crl_list->tbs.p, crl_list->tbs.len, hash );
-
- if( x509_profile_check_key( profile, crl_list->sig_pk, &ca->pk ) != 0 )
+ if( x509_profile_check_key( profile, &ca->pk ) != 0 )
flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
if( mbedtls_pk_verify_ext( crl_list->sig_pk, crl_list->sig_opts, &ca->pk,
@@ -1786,13 +1794,14 @@ static int x509_memcasecmp( const void *s1, const void *s2, size_t len )
/*
* Return 0 if name matches wildcard, -1 otherwise
*/
-static int x509_check_wildcard( const char *cn, mbedtls_x509_buf *name )
+static int x509_check_wildcard( const char *cn, const mbedtls_x509_buf *name )
{
size_t i;
size_t cn_idx = 0, cn_len = strlen( cn );
+ /* We can't have a match if there is no wildcard to match */
if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' )
- return( 0 );
+ return( -1 );
for( i = 0; i < cn_len; ++i )
{
@@ -1884,15 +1893,40 @@ static int x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b
}
/*
+ * Check the signature of a certificate by its parent
+ */
+static int x509_crt_check_signature( const mbedtls_x509_crt *child,
+ mbedtls_x509_crt *parent )
+{
+ const mbedtls_md_info_t *md_info;
+ unsigned char hash[MBEDTLS_MD_MAX_SIZE];
+
+ md_info = mbedtls_md_info_from_type( child->sig_md );
+ if( mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash ) != 0 )
+ {
+ /* Note: this can't happen except after an internal error */
+ return( -1 );
+ }
+
+ if( mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent->pk,
+ child->sig_md, hash, mbedtls_md_get_size( md_info ),
+ child->sig.p, child->sig.len ) != 0 )
+ {
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+/*
* Check if 'parent' is a suitable parent (signing CA) for 'child'.
* Return 0 if yes, -1 if not.
*
* top means parent is a locally-trusted certificate
- * bottom means child is the end entity cert
*/
static int x509_crt_check_parent( const mbedtls_x509_crt *child,
const mbedtls_x509_crt *parent,
- int top, int bottom )
+ int top )
{
int need_ca_bit;
@@ -1907,14 +1941,6 @@ static int x509_crt_check_parent( const mbedtls_x509_crt *child,
if( top && parent->version < 3 )
need_ca_bit = 0;
- /* Exception: self-signed end-entity certs that are locally trusted. */
- if( top && bottom &&
- child->raw.len == parent->raw.len &&
- memcmp( child->raw.p, parent->raw.p, child->raw.len ) == 0 )
- {
- need_ca_bit = 0;
- }
-
if( need_ca_bit && ! parent->ca_istrue )
return( -1 );
@@ -1929,86 +1955,78 @@ static int x509_crt_check_parent( const mbedtls_x509_crt *child,
return( 0 );
}
-static int x509_crt_verify_top(
- mbedtls_x509_crt *child, mbedtls_x509_crt *trust_ca,
- mbedtls_x509_crl *ca_crl,
- const mbedtls_x509_crt_profile *profile,
- int path_cnt, int self_cnt, uint32_t *flags,
- int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
- void *p_vrfy )
+/*
+ * Find a suitable parent for child in candidates, or return NULL.
+ *
+ * Here suitable is defined as:
+ * 1. subject name matches child's issuer
+ * 2. if necessary, the CA bit is set and key usage allows signing certs
+ * 3. for trusted roots, the signature is correct
+ * 4. pathlen constraints are satisfied
+ *
+ * If there's a suitable candidate which is also time-valid, return the first
+ * such. Otherwise, return the first suitable candidate (or NULL if there is
+ * none).
+ *
+ * The rationale for this rule is that someone could have a list of trusted
+ * roots with two versions on the same root with different validity periods.
+ * (At least one user reported having such a list and wanted it to just work.)
+ * The reason we don't just require time-validity is that generally there is
+ * only one version, and if it's expired we want the flags to state that
+ * rather than NOT_TRUSTED, as would be the case if we required it here.
+ *
+ * The rationale for rule 3 (signature for trusted roots) is that users might
+ * have two versions of the same CA with different keys in their list, and the
+ * way we select the correct one is by checking the signature (as we don't
+ * rely on key identifier extensions). (This is one way users might choose to
+ * handle key rollover, another relies on self-issued certs, see [SIRO].)
+ *
+ * Arguments:
+ * - [in] child: certificate for which we're looking for a parent
+ * - [in] candidates: chained list of potential parents
+ * - [in] top: 1 if candidates consists of trusted roots, ie we're at the top
+ * of the chain, 0 otherwise
+ * - [in] path_cnt: number of intermediates seen so far
+ * - [in] self_cnt: number of self-signed intermediates seen so far
+ * (will never be greater than path_cnt)
+ *
+ * Return value:
+ * - the first suitable parent found (see above regarding time-validity)
+ * - NULL if no suitable parent was found
+ */
+static mbedtls_x509_crt *x509_crt_find_parent_in( mbedtls_x509_crt *child,
+ mbedtls_x509_crt *candidates,
+ int top,
+ size_t path_cnt,
+ size_t self_cnt )
{
- int ret;
- uint32_t ca_flags = 0;
- int check_path_cnt;
- unsigned char hash[MBEDTLS_MD_MAX_SIZE];
- const mbedtls_md_info_t *md_info;
- mbedtls_x509_crt *future_past_ca = NULL;
+ mbedtls_x509_crt *parent, *badtime_parent = NULL;
- if( mbedtls_x509_time_is_past( &child->valid_to ) )
- *flags |= MBEDTLS_X509_BADCERT_EXPIRED;
-
- if( mbedtls_x509_time_is_future( &child->valid_from ) )
- *flags |= MBEDTLS_X509_BADCERT_FUTURE;
-
- if( x509_profile_check_md_alg( profile, child->sig_md ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_MD;
-
- if( x509_profile_check_pk_alg( profile, child->sig_pk ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_PK;
-
- /*
- * Child is the top of the chain. Check against the trust_ca list.
- */
- *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
-
- md_info = mbedtls_md_info_from_type( child->sig_md );
- if( md_info == NULL )
+ for( parent = candidates; parent != NULL; parent = parent->next )
{
- /*
- * Cannot check 'unknown', no need to try any CA
- */
- trust_ca = NULL;
- }
- else
- mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash );
-
- for( /* trust_ca */ ; trust_ca != NULL; trust_ca = trust_ca->next )
- {
- if( x509_crt_check_parent( child, trust_ca, 1, path_cnt == 0 ) != 0 )
+ /* basic parenting skills (name, CA bit, key usage) */
+ if( x509_crt_check_parent( child, parent, top ) != 0 )
continue;
- check_path_cnt = path_cnt + 1;
-
- /*
- * Reduce check_path_cnt to check against if top of the chain is
- * the same as the trusted CA
- */
- if( child->subject_raw.len == trust_ca->subject_raw.len &&
- memcmp( child->subject_raw.p, trust_ca->subject_raw.p,
- child->issuer_raw.len ) == 0 )
- {
- check_path_cnt--;
- }
-
- /* Self signed certificates do not count towards the limit */
- if( trust_ca->max_pathlen > 0 &&
- trust_ca->max_pathlen < check_path_cnt - self_cnt )
+ /* +1 because stored max_pathlen is 1 higher that the actual value */
+ if( parent->max_pathlen > 0 &&
+ (size_t) parent->max_pathlen < 1 + path_cnt - self_cnt )
{
continue;
}
- if( mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &trust_ca->pk,
- child->sig_md, hash, mbedtls_md_get_size( md_info ),
- child->sig.p, child->sig.len ) != 0 )
+ /* Signature */
+ if( top && x509_crt_check_signature( child, parent ) != 0 )
{
continue;
}
- if( mbedtls_x509_time_is_past( &trust_ca->valid_to ) ||
- mbedtls_x509_time_is_future( &trust_ca->valid_from ) )
+ /* optional time check */
+ if( mbedtls_x509_time_is_past( &parent->valid_to ) ||
+ mbedtls_x509_time_is_future( &parent->valid_from ) )
{
- if ( future_past_ca == NULL )
- future_past_ca = trust_ca;
+ if( badtime_parent == NULL )
+ badtime_parent = parent;
continue;
}
@@ -2016,190 +2034,292 @@ static int x509_crt_verify_top(
break;
}
- if( trust_ca != NULL || ( trust_ca = future_past_ca ) != NULL )
- {
- /*
- * Top of chain is signed by a trusted CA
- */
- *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED;
+ if( parent == NULL )
+ parent = badtime_parent;
- if( x509_profile_check_key( profile, child->sig_pk, &trust_ca->pk ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
- }
+ return( parent );
+}
- /*
- * If top of chain is not the same as the trusted CA send a verify request
- * to the callback for any issues with validity and CRL presence for the
- * trusted CA certificate.
- */
- if( trust_ca != NULL &&
- ( child->subject_raw.len != trust_ca->subject_raw.len ||
- memcmp( child->subject_raw.p, trust_ca->subject_raw.p,
- child->issuer_raw.len ) != 0 ) )
- {
-#if defined(MBEDTLS_X509_CRL_PARSE_C)
- /* Check trusted CA's CRL for the chain's top crt */
- *flags |= x509_crt_verifycrl( child, trust_ca, ca_crl, profile );
-#else
- ((void) ca_crl);
-#endif
+/*
+ * Find a parent in trusted CAs or the provided chain, or return NULL.
+ *
+ * Searches in trusted CAs first, and return the first suitable parent found
+ * (see find_parent_in() for definition of suitable).
+ *
+ * Arguments:
+ * - [in] child: certificate for which we're looking for a parent, followed
+ * by a chain of possible intermediates
+ * - [in] trust_ca: locally trusted CAs
+ * - [out] 1 if parent was found in trust_ca, 0 if found in provided chain
+ * - [in] path_cnt: number of intermediates seen so far
+ * - [in] self_cnt: number of self-signed intermediates seen so far
+ * (will always be no greater than path_cnt)
+ *
+ * Return value:
+ * - the first suitable parent found (see find_parent_in() for "suitable")
+ * - NULL if no suitable parent was found
+ */
+static mbedtls_x509_crt *x509_crt_find_parent( mbedtls_x509_crt *child,
+ mbedtls_x509_crt *trust_ca,
+ int *parent_is_trusted,
+ size_t path_cnt,
+ size_t self_cnt )
+{
+ mbedtls_x509_crt *parent;
- if( mbedtls_x509_time_is_past( &trust_ca->valid_to ) )
- ca_flags |= MBEDTLS_X509_BADCERT_EXPIRED;
+ /* Look for a parent in trusted CAs */
+ *parent_is_trusted = 1;
+ parent = x509_crt_find_parent_in( child, trust_ca, 1, path_cnt, self_cnt );
- if( mbedtls_x509_time_is_future( &trust_ca->valid_from ) )
- ca_flags |= MBEDTLS_X509_BADCERT_FUTURE;
+ if( parent != NULL )
+ return( parent );
- if( NULL != f_vrfy )
- {
- if( ( ret = f_vrfy( p_vrfy, trust_ca, path_cnt + 1,
- &ca_flags ) ) != 0 )
- {
- return( ret );
- }
- }
- }
+ /* Look for a parent upwards the chain */
+ *parent_is_trusted = 0;
+ return( x509_crt_find_parent_in( child, child->next, 0, path_cnt, self_cnt ) );
+}
- /* Call callback on top cert */
- if( NULL != f_vrfy )
+/*
+ * Check if an end-entity certificate is locally trusted
+ *
+ * Currently we require such certificates to be self-signed (actually only
+ * check for self-issued as self-signatures are not checked)
+ */
+static int x509_crt_check_ee_locally_trusted(
+ mbedtls_x509_crt *crt,
+ mbedtls_x509_crt *trust_ca )
+{
+ mbedtls_x509_crt *cur;
+
+ /* must be self-issued */
+ if( x509_name_cmp( &crt->issuer, &crt->subject ) != 0 )
+ return( -1 );
+
+ /* look for an exact match with trusted cert */
+ for( cur = trust_ca; cur != NULL; cur = cur->next )
{
- if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 )
- return( ret );
+ if( crt->raw.len == cur->raw.len &&
+ memcmp( crt->raw.p, cur->raw.p, crt->raw.len ) == 0 )
+ {
+ return( 0 );
+ }
}
- *flags |= ca_flags;
-
- return( 0 );
+ /* too bad */
+ return( -1 );
}
-static int x509_crt_verify_child(
- mbedtls_x509_crt *child, mbedtls_x509_crt *parent,
- mbedtls_x509_crt *trust_ca, mbedtls_x509_crl *ca_crl,
+/*
+ * Build and verify a certificate chain
+ *
+ * Given a peer-provided list of certificates EE, C1, ..., Cn and
+ * a list of trusted certs R1, ... Rp, try to build and verify a chain
+ * EE, Ci1, ... Ciq [, Rj]
+ * such that every cert in the chain is a child of the next one,
+ * jumping to a trusted root as early as possible.
+ *
+ * Verify that chain and return it with flags for all issues found.
+ *
+ * Special cases:
+ * - EE == Rj -> return a one-element list containing it
+ * - EE, Ci1, ..., Ciq cannot be continued with a trusted root
+ * -> return that chain with NOT_TRUSTED set on Ciq
+ *
+ * Arguments:
+ * - [in] crt: the cert list EE, C1, ..., Cn
+ * - [in] trust_ca: the trusted list R1, ..., Rp
+ * - [in] ca_crl, profile: as in verify_with_profile()
+ * - [out] ver_chain, chain_len: the built and verified chain
+ *
+ * Return value:
+ * - non-zero if the chain could not be fully built and examined
+ * - 0 is the chain was successfully built and examined,
+ * even if it was found to be invalid
+ */
+static int x509_crt_verify_chain(
+ mbedtls_x509_crt *crt,
+ mbedtls_x509_crt *trust_ca,
+ mbedtls_x509_crl *ca_crl,
const mbedtls_x509_crt_profile *profile,
- int path_cnt, int self_cnt, uint32_t *flags,
- int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
- void *p_vrfy )
+ x509_crt_verify_chain_item ver_chain[X509_MAX_VERIFY_CHAIN_SIZE],
+ size_t *chain_len )
{
- int ret;
- uint32_t parent_flags = 0;
- unsigned char hash[MBEDTLS_MD_MAX_SIZE];
- mbedtls_x509_crt *grandparent;
- const mbedtls_md_info_t *md_info;
+ uint32_t *flags;
+ mbedtls_x509_crt *child;
+ mbedtls_x509_crt *parent;
+ int parent_is_trusted = 0;
+ int child_is_trusted = 0;
+ size_t self_cnt = 0;
- /* Counting intermediate self signed certificates */
- if( ( path_cnt != 0 ) && x509_name_cmp( &child->issuer, &child->subject ) == 0 )
- self_cnt++;
+ child = crt;
+ *chain_len = 0;
- /* path_cnt is 0 for the first intermediate CA */
- if( 1 + path_cnt > MBEDTLS_X509_MAX_INTERMEDIATE_CA )
- {
- /* return immediately as the goal is to avoid unbounded recursion */
- return( MBEDTLS_ERR_X509_FATAL_ERROR );
- }
+ while( 1 ) {
+ /* Add certificate to the verification chain */
+ ver_chain[*chain_len].crt = child;
+ flags = &ver_chain[*chain_len].flags;
+ ++*chain_len;
- if( mbedtls_x509_time_is_past( &child->valid_to ) )
- *flags |= MBEDTLS_X509_BADCERT_EXPIRED;
+ /* Check time-validity (all certificates) */
+ if( mbedtls_x509_time_is_past( &child->valid_to ) )
+ *flags |= MBEDTLS_X509_BADCERT_EXPIRED;
- if( mbedtls_x509_time_is_future( &child->valid_from ) )
- *flags |= MBEDTLS_X509_BADCERT_FUTURE;
+ if( mbedtls_x509_time_is_future( &child->valid_from ) )
+ *flags |= MBEDTLS_X509_BADCERT_FUTURE;
- if( x509_profile_check_md_alg( profile, child->sig_md ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_MD;
+ /* Stop here for trusted roots (but not for trusted EE certs) */
+ if( child_is_trusted )
+ return( 0 );
- if( x509_profile_check_pk_alg( profile, child->sig_pk ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_PK;
+ /* Check signature algorithm: MD & PK algs */
+ if( x509_profile_check_md_alg( profile, child->sig_md ) != 0 )
+ *flags |= MBEDTLS_X509_BADCERT_BAD_MD;
- md_info = mbedtls_md_info_from_type( child->sig_md );
- if( md_info == NULL )
- {
- /*
- * Cannot check 'unknown' hash
- */
- *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
- }
- else
- {
- mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash );
+ if( x509_profile_check_pk_alg( profile, child->sig_pk ) != 0 )
+ *flags |= MBEDTLS_X509_BADCERT_BAD_PK;
- if( x509_profile_check_key( profile, child->sig_pk, &parent->pk ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
+ /* Special case: EE certs that are locally trusted */
+ if( *chain_len == 1 &&
+ x509_crt_check_ee_locally_trusted( child, trust_ca ) == 0 )
+ {
+ return( 0 );
+ }
- if( mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent->pk,
- child->sig_md, hash, mbedtls_md_get_size( md_info ),
- child->sig.p, child->sig.len ) != 0 )
+ /* Look for a parent in trusted CAs or up the chain */
+ parent = x509_crt_find_parent( child, trust_ca, &parent_is_trusted,
+ *chain_len - 1, self_cnt );
+
+ /* No parent? We're done here */
+ if( parent == NULL )
{
*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
+ return( 0 );
+ }
+
+ /* Count intermediate self-issued (not necessarily self-signed) certs.
+ * These can occur with some strategies for key rollover, see [SIRO],
+ * and should be excluded from max_pathlen checks. */
+ if( *chain_len != 1 &&
+ x509_name_cmp( &child->issuer, &child->subject ) == 0 )
+ {
+ self_cnt++;
}
- }
+
+ /* path_cnt is 0 for the first intermediate CA,
+ * and if parent is trusted it's not an intermediate CA */
+ if( ! parent_is_trusted &&
+ *chain_len > MBEDTLS_X509_MAX_INTERMEDIATE_CA )
+ {
+ /* return immediately to avoid overflow the chain array */
+ return( MBEDTLS_ERR_X509_FATAL_ERROR );
+ }
+
+ /* if parent is trusted, the signature was checked by find_parent() */
+ if( ! parent_is_trusted && x509_crt_check_signature( child, parent ) != 0 )
+ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
+
+ /* check size of signing key */
+ if( x509_profile_check_key( profile, &parent->pk ) != 0 )
+ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
#if defined(MBEDTLS_X509_CRL_PARSE_C)
- /* Check trusted CA's CRL for the given crt */
- *flags |= x509_crt_verifycrl(child, parent, ca_crl, profile );
+ /* Check trusted CA's CRL for the given crt */
+ *flags |= x509_crt_verifycrl( child, parent, ca_crl, profile );
+#else
+ (void) ca_crl;
#endif
- /* Look for a grandparent in trusted CAs */
- for( grandparent = trust_ca;
- grandparent != NULL;
- grandparent = grandparent->next )
+ /* prepare for next iteration */
+ child = parent;
+ parent = NULL;
+ child_is_trusted = parent_is_trusted;
+ }
+}
+
+/*
+ * Check for CN match
+ */
+static int x509_crt_check_cn( const mbedtls_x509_buf *name,
+ const char *cn, size_t cn_len )
+{
+ /* try exact match */
+ if( name->len == cn_len &&
+ x509_memcasecmp( cn, name->p, cn_len ) == 0 )
{
- if( x509_crt_check_parent( parent, grandparent,
- 0, path_cnt == 0 ) == 0 )
- break;
+ return( 0 );
}
- if( grandparent != NULL )
+ /* try wildcard match */
+ if( x509_check_wildcard( cn, name ) == 0 )
{
- ret = x509_crt_verify_top( parent, grandparent, ca_crl, profile,
- path_cnt + 1, self_cnt, &parent_flags, f_vrfy, p_vrfy );
- if( ret != 0 )
- return( ret );
+ return( 0 );
}
- else
+
+ return( -1 );
+}
+
+/*
+ * Verify the requested CN - only call this if cn is not NULL!
+ */
+static void x509_crt_verify_name( const mbedtls_x509_crt *crt,
+ const char *cn,
+ uint32_t *flags )
+{
+ const mbedtls_x509_name *name;
+ const mbedtls_x509_sequence *cur;
+ size_t cn_len = strlen( cn );
+
+ if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME )
{
- /* Look for a grandparent upwards the chain */
- for( grandparent = parent->next;
- grandparent != NULL;
- grandparent = grandparent->next )
+ for( cur = &crt->subject_alt_names; cur != NULL; cur = cur->next )
{
- /* +2 because the current step is not yet accounted for
- * and because max_pathlen is one higher than it should be.
- * Also self signed certificates do not count to the limit. */
- if( grandparent->max_pathlen > 0 &&
- grandparent->max_pathlen < 2 + path_cnt - self_cnt )
- {
- continue;
- }
-
- if( x509_crt_check_parent( parent, grandparent,
- 0, path_cnt == 0 ) == 0 )
+ if( x509_crt_check_cn( &cur->buf, cn, cn_len ) == 0 )
break;
}
- /* Is our parent part of the chain or at the top? */
- if( grandparent != NULL )
- {
- ret = x509_crt_verify_child( parent, grandparent, trust_ca, ca_crl,
- profile, path_cnt + 1, self_cnt, &parent_flags,
- f_vrfy, p_vrfy );
- if( ret != 0 )
- return( ret );
- }
- else
+ if( cur == NULL )
+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+ }
+ else
+ {
+ for( name = &crt->subject; name != NULL; name = name->next )
{
- ret = x509_crt_verify_top( parent, trust_ca, ca_crl, profile,
- path_cnt + 1, self_cnt, &parent_flags,
- f_vrfy, p_vrfy );
- if( ret != 0 )
- return( ret );
+ if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, &name->oid ) == 0 &&
+ x509_crt_check_cn( &name->val, cn, cn_len ) == 0 )
+ {
+ break;
+ }
}
+
+ if( name == NULL )
+ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
}
+}
- /* child is verified to be a child of the parent, call verify callback */
- if( NULL != f_vrfy )
- if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 )
- return( ret );
+/*
+ * Merge the flags for all certs in the chain, after calling callback
+ */
+static int x509_crt_merge_flags_with_cb(
+ uint32_t *flags,
+ x509_crt_verify_chain_item ver_chain[X509_MAX_VERIFY_CHAIN_SIZE],
+ size_t chain_len,
+ int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
+ void *p_vrfy )
+{
+ int ret;
+ size_t i;
+ uint32_t cur_flags;
+
+ for( i = chain_len; i != 0; --i )
+ {
+ cur_flags = ver_chain[i-1].flags;
+
+ if( NULL != f_vrfy )
+ if( ( ret = f_vrfy( p_vrfy, ver_chain[i-1].crt, (int) i-1, &cur_flags ) ) != 0 )
+ return( ret );
- *flags |= parent_flags;
+ *flags |= cur_flags;
+ }
return( 0 );
}
@@ -2218,9 +2338,15 @@ int mbedtls_x509_crt_verify( mbedtls_x509_crt *crt,
&mbedtls_x509_crt_profile_default, cn, flags, f_vrfy, p_vrfy ) );
}
-
/*
* Verify the certificate validity, with profile
+ *
+ * This function:
+ * - checks the requested CN (if any)
+ * - checks the type and size of the EE cert's key,
+ * as that isn't done as part of chain building/verification currently
+ * - builds and verifies the chain
+ * - then calls the callback and merges the flags
*/
int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
mbedtls_x509_crt *trust_ca,
@@ -2230,15 +2356,15 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *),
void *p_vrfy )
{
- size_t cn_len;
int ret;
- int pathlen = 0, selfsigned = 0;
- mbedtls_x509_crt *parent;
- mbedtls_x509_name *name;
- mbedtls_x509_sequence *cur = NULL;
mbedtls_pk_type_t pk_type;
+ x509_crt_verify_chain_item ver_chain[X509_MAX_VERIFY_CHAIN_SIZE];
+ size_t chain_len;
+ uint32_t *ee_flags = &ver_chain[0].flags;
*flags = 0;
+ memset( ver_chain, 0, sizeof( ver_chain ) );
+ chain_len = 0;
if( profile == NULL )
{
@@ -2246,104 +2372,28 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
goto exit;
}
+ /* check name if requested */
if( cn != NULL )
- {
- name = &crt->subject;
- cn_len = strlen( cn );
-
- if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME )
- {
- cur = &crt->subject_alt_names;
-
- while( cur != NULL )
- {
- if( cur->buf.len == cn_len &&
- x509_memcasecmp( cn, cur->buf.p, cn_len ) == 0 )
- break;
-
- if( cur->buf.len > 2 &&
- memcmp( cur->buf.p, "*.", 2 ) == 0 &&
- x509_check_wildcard( cn, &cur->buf ) == 0 )
- {
- break;
- }
-
- cur = cur->next;
- }
-
- if( cur == NULL )
- *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
- }
- else
- {
- while( name != NULL )
- {
- if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, &name->oid ) == 0 )
- {
- if( name->val.len == cn_len &&
- x509_memcasecmp( name->val.p, cn, cn_len ) == 0 )
- break;
-
- if( name->val.len > 2 &&
- memcmp( name->val.p, "*.", 2 ) == 0 &&
- x509_check_wildcard( cn, &name->val ) == 0 )
- break;
- }
-
- name = name->next;
- }
-
- if( name == NULL )
- *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
- }
- }
+ x509_crt_verify_name( crt, cn, ee_flags );
/* Check the type and size of the key */
pk_type = mbedtls_pk_get_type( &crt->pk );
if( x509_profile_check_pk_alg( profile, pk_type ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_PK;
+ *ee_flags |= MBEDTLS_X509_BADCERT_BAD_PK;
- if( x509_profile_check_key( profile, pk_type, &crt->pk ) != 0 )
- *flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
+ if( x509_profile_check_key( profile, &crt->pk ) != 0 )
+ *ee_flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
- /* Look for a parent in trusted CAs */
- for( parent = trust_ca; parent != NULL; parent = parent->next )
- {
- if( x509_crt_check_parent( crt, parent, 0, pathlen == 0 ) == 0 )
- break;
- }
-
- if( parent != NULL )
- {
- ret = x509_crt_verify_top( crt, parent, ca_crl, profile,
- pathlen, selfsigned, flags, f_vrfy, p_vrfy );
- if( ret != 0 )
- goto exit;
- }
- else
- {
- /* Look for a parent upwards the chain */
- for( parent = crt->next; parent != NULL; parent = parent->next )
- if( x509_crt_check_parent( crt, parent, 0, pathlen == 0 ) == 0 )
- break;
+ /* Check the chain */
+ ret = x509_crt_verify_chain( crt, trust_ca, ca_crl, profile,
+ ver_chain, &chain_len );
+ if( ret != 0 )
+ goto exit;
- /* Are we part of the chain or at the top? */
- if( parent != NULL )
- {
- ret = x509_crt_verify_child( crt, parent, trust_ca, ca_crl, profile,
- pathlen, selfsigned, flags, f_vrfy, p_vrfy );
- if( ret != 0 )
- goto exit;
- }
- else
- {
- ret = x509_crt_verify_top( crt, trust_ca, ca_crl, profile,
- pathlen, selfsigned, flags, f_vrfy, p_vrfy );
- if( ret != 0 )
- goto exit;
- }
- }
+ /* Build final flags, calling callback on the way if any */
+ ret = x509_crt_merge_flags_with_cb( flags,
+ ver_chain, chain_len, f_vrfy, p_vrfy );
exit:
/* prevent misuse of the vrfy callback - VERIFY_FAILED would be ignored by
@@ -2400,7 +2450,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
{
name_prv = name_cur;
name_cur = name_cur->next;
- mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
+ mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
mbedtls_free( name_prv );
}
@@ -2409,7 +2459,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
{
name_prv = name_cur;
name_cur = name_cur->next;
- mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
+ mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
mbedtls_free( name_prv );
}
@@ -2418,7 +2468,8 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
{
seq_prv = seq_cur;
seq_cur = seq_cur->next;
- mbedtls_zeroize( seq_prv, sizeof( mbedtls_x509_sequence ) );
+ mbedtls_platform_zeroize( seq_prv,
+ sizeof( mbedtls_x509_sequence ) );
mbedtls_free( seq_prv );
}
@@ -2427,13 +2478,14 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
{
seq_prv = seq_cur;
seq_cur = seq_cur->next;
- mbedtls_zeroize( seq_prv, sizeof( mbedtls_x509_sequence ) );
+ mbedtls_platform_zeroize( seq_prv,
+ sizeof( mbedtls_x509_sequence ) );
mbedtls_free( seq_prv );
}
if( cert_cur->raw.p != NULL )
{
- mbedtls_zeroize( cert_cur->raw.p, cert_cur->raw.len );
+ mbedtls_platform_zeroize( cert_cur->raw.p, cert_cur->raw.len );
mbedtls_free( cert_cur->raw.p );
}
@@ -2447,7 +2499,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
cert_prv = cert_cur;
cert_cur = cert_cur->next;
- mbedtls_zeroize( cert_prv, sizeof( mbedtls_x509_crt ) );
+ mbedtls_platform_zeroize( cert_prv, sizeof( mbedtls_x509_crt ) );
if( cert_prv != crt )
mbedtls_free( cert_prv );
}
diff --git a/thirdparty/mbedtls/library/x509_csr.c b/thirdparty/mbedtls/library/x509_csr.c
index 26a06db4f6..3e8e8fbc6a 100644
--- a/thirdparty/mbedtls/library/x509_csr.c
+++ b/thirdparty/mbedtls/library/x509_csr.c
@@ -39,6 +39,7 @@
#include "mbedtls/x509_csr.h"
#include "mbedtls/oid.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -60,11 +61,6 @@
#include <stdio.h>
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* Version ::= INTEGER { v1(0) }
*/
@@ -325,7 +321,7 @@ int mbedtls_x509_csr_parse_file( mbedtls_x509_csr *csr, const char *path )
ret = mbedtls_x509_csr_parse( csr, buf, n );
- mbedtls_zeroize( buf, n );
+ mbedtls_platform_zeroize( buf, n );
mbedtls_free( buf );
return( ret );
@@ -407,17 +403,17 @@ void mbedtls_x509_csr_free( mbedtls_x509_csr *csr )
{
name_prv = name_cur;
name_cur = name_cur->next;
- mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
+ mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
mbedtls_free( name_prv );
}
if( csr->raw.p != NULL )
{
- mbedtls_zeroize( csr->raw.p, csr->raw.len );
+ mbedtls_platform_zeroize( csr->raw.p, csr->raw.len );
mbedtls_free( csr->raw.p );
}
- mbedtls_zeroize( csr, sizeof( mbedtls_x509_csr ) );
+ mbedtls_platform_zeroize( csr, sizeof( mbedtls_x509_csr ) );
}
#endif /* MBEDTLS_X509_CSR_PARSE_C */
diff --git a/thirdparty/mbedtls/library/x509write_crt.c b/thirdparty/mbedtls/library/x509write_crt.c
index 41dfe87b75..b1ef216c95 100644
--- a/thirdparty/mbedtls/library/x509write_crt.c
+++ b/thirdparty/mbedtls/library/x509write_crt.c
@@ -37,6 +37,7 @@
#include "mbedtls/oid.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/sha1.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -44,11 +45,6 @@
#include "mbedtls/pem.h"
#endif /* MBEDTLS_PEM_WRITE_C */
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
void mbedtls_x509write_crt_init( mbedtls_x509write_cert *ctx )
{
memset( ctx, 0, sizeof( mbedtls_x509write_cert ) );
@@ -65,7 +61,7 @@ void mbedtls_x509write_crt_free( mbedtls_x509write_cert *ctx )
mbedtls_asn1_free_named_data_list( &ctx->issuer );
mbedtls_asn1_free_named_data_list( &ctx->extensions );
- mbedtls_zeroize( ctx, sizeof( mbedtls_x509write_cert ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x509write_cert ) );
}
void mbedtls_x509write_crt_set_version( mbedtls_x509write_cert *ctx, int version )
diff --git a/thirdparty/mbedtls/library/x509write_csr.c b/thirdparty/mbedtls/library/x509write_csr.c
index e80053828f..66cee56014 100644
--- a/thirdparty/mbedtls/library/x509write_csr.c
+++ b/thirdparty/mbedtls/library/x509write_csr.c
@@ -35,6 +35,7 @@
#include "mbedtls/x509_csr.h"
#include "mbedtls/oid.h"
#include "mbedtls/asn1write.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
#include <stdlib.h>
@@ -43,11 +44,6 @@
#include "mbedtls/pem.h"
#endif
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
void mbedtls_x509write_csr_init( mbedtls_x509write_csr *ctx )
{
memset( ctx, 0, sizeof( mbedtls_x509write_csr ) );
@@ -58,7 +54,7 @@ void mbedtls_x509write_csr_free( mbedtls_x509write_csr *ctx )
mbedtls_asn1_free_named_data_list( &ctx->subject );
mbedtls_asn1_free_named_data_list( &ctx->extensions );
- mbedtls_zeroize( ctx, sizeof( mbedtls_x509write_csr ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x509write_csr ) );
}
void mbedtls_x509write_csr_set_md_alg( mbedtls_x509write_csr *ctx, mbedtls_md_type_t md_alg )
diff --git a/thirdparty/mbedtls/library/xtea.c b/thirdparty/mbedtls/library/xtea.c
index fe0a3509f6..a33707bc17 100644
--- a/thirdparty/mbedtls/library/xtea.c
+++ b/thirdparty/mbedtls/library/xtea.c
@@ -28,6 +28,7 @@
#if defined(MBEDTLS_XTEA_C)
#include "mbedtls/xtea.h"
+#include "mbedtls/platform_util.h"
#include <string.h>
@@ -42,11 +43,6 @@
#if !defined(MBEDTLS_XTEA_ALT)
-/* Implementation that should never be optimized out by the compiler */
-static void mbedtls_zeroize( void *v, size_t n ) {
- volatile unsigned char *p = v; while( n-- ) *p++ = 0;
-}
-
/*
* 32-bit integer manipulation macros (big endian)
*/
@@ -80,7 +76,7 @@ void mbedtls_xtea_free( mbedtls_xtea_context *ctx )
if( ctx == NULL )
return;
- mbedtls_zeroize( ctx, sizeof( mbedtls_xtea_context ) );
+ mbedtls_platform_zeroize( ctx, sizeof( mbedtls_xtea_context ) );
}
/*
diff --git a/thirdparty/pcre2/AUTHORS b/thirdparty/pcre2/AUTHORS
index e056ad6868..d5592bbc5b 100644
--- a/thirdparty/pcre2/AUTHORS
+++ b/thirdparty/pcre2/AUTHORS
@@ -8,7 +8,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2017 University of Cambridge
+Copyright (c) 1997-2018 University of Cambridge
All rights reserved
@@ -19,7 +19,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2017 Zoltan Herczeg
+Copyright(c) 2010-2018 Zoltan Herczeg
All rights reserved.
@@ -30,7 +30,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2017 Zoltan Herczeg
+Copyright(c) 2009-2018 Zoltan Herczeg
All rights reserved.
####
diff --git a/thirdparty/pcre2/LICENCE b/thirdparty/pcre2/LICENCE
index 402fe2435c..bfe3c8d528 100644
--- a/thirdparty/pcre2/LICENCE
+++ b/thirdparty/pcre2/LICENCE
@@ -5,9 +5,10 @@ PCRE2 is a library of functions to support regular expressions whose syntax
and semantics are as close as possible to those of the Perl 5 language.
Release 10 of PCRE2 is distributed under the terms of the "BSD" licence, as
-specified below. The documentation for PCRE2, supplied in the "doc"
-directory, is distributed under the same terms as the software itself. The data
-in the testdata directory is not copyrighted and is in the public domain.
+specified below, with one exemption for certain binary redistributions. The
+documentation for PCRE2, supplied in the "doc" directory, is distributed under
+the same terms as the software itself. The data in the testdata directory is
+not copyrighted and is in the public domain.
The basic library functions are written in C and are freestanding. Also
included in the distribution is a just-in-time compiler that can be used to
@@ -25,7 +26,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2017 University of Cambridge
+Copyright (c) 1997-2018 University of Cambridge
All rights reserved.
@@ -36,7 +37,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2017 Zoltan Herczeg
+Copyright(c) 2010-2018 Zoltan Herczeg
All rights reserved.
@@ -47,7 +48,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2017 Zoltan Herczeg
+Copyright(c) 2009-2018 Zoltan Herczeg
All rights reserved.
@@ -57,11 +58,11 @@ THE "BSD" LICENCE
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
+ * Redistributions of source code must retain the above copyright notices,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
+ notices, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the University of Cambridge nor the names of any
@@ -80,4 +81,14 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+
+EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES
+------------------------------------------
+
+The second condition in the BSD licence (covering binary redistributions) does
+not apply all the way down a chain of software. If binary package A includes
+PCRE2, it must respect the condition, but if package B is software that
+includes package A, the condition is not imposed on package B unless it uses
+PCRE2 independently.
+
End
diff --git a/thirdparty/pcre2/src/config.h b/thirdparty/pcre2/src/config.h
index 3315b7770e..f738616714 100644
--- a/thirdparty/pcre2/src/config.h
+++ b/thirdparty/pcre2/src/config.h
@@ -132,13 +132,11 @@ sure both macros are undefined; an emulation function will then be used. */
/* Define to 1 if you have the <zlib.h> header file. */
/* #undef HAVE_ZLIB_H */
-/* PCRE2 uses recursive function calls to handle backtracking while matching.
- This can sometimes be a problem on systems that have stacks of limited
- size. Define HEAP_MATCH_RECURSE to any value to get a version that doesn't
- use recursion in the match() function; instead it creates its own stack by
- steam using memory from the heap. For more detail, see the comments and
- other stuff just above the match() function. */
-/* #undef HEAP_MATCH_RECURSE */
+/* This limits the amount of memory that pcre2_match() may use while matching
+ a pattern. The value is in kilobytes. */
+#ifndef HEAP_LIMIT
+#define HEAP_LIMIT 20000000
+#endif
/* The value of LINK_SIZE determines the number of bytes used to store links
as offsets within the compiled regex. The default is 2, which allows for
@@ -156,25 +154,25 @@ sure both macros are undefined; an emulation function will then be used. */
#endif
/* The value of MATCH_LIMIT determines the default number of times the
- internal match() function can be called during a single execution of
- pcre2_match(). There is a runtime interface for setting a different limit.
- The limit exists in order to catch runaway regular expressions that take
- for ever to determine that they do not match. The default is set very large
- so that it does not accidentally catch legitimate cases. */
+ pcre2_match() function can record a backtrack position during a single
+ matching attempt. There is a runtime interface for setting a different
+ limit. The limit exists in order to catch runaway regular expressions that
+ take for ever to determine that they do not match. The default is set very
+ large so that it does not accidentally catch legitimate cases. */
#ifndef MATCH_LIMIT
#define MATCH_LIMIT 10000000
#endif
-/* The above limit applies to all calls of match(), whether or not they
- increase the recursion depth. In some environments it is desirable to limit
- the depth of recursive calls of match() more strictly, in order to restrict
- the maximum amount of stack (or heap, if HEAP_MATCH_RECURSE is defined)
- that is used. The value of MATCH_LIMIT_RECURSION applies only to recursive
- calls of match(). To have any useful effect, it must be less than the value
- of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There
- is a runtime method for setting a different limit. */
-#ifndef MATCH_LIMIT_RECURSION
-#define MATCH_LIMIT_RECURSION MATCH_LIMIT
+/* The above limit applies to all backtracks, whether or not they are nested.
+ In some environments it is desirable to limit the nesting of backtracking
+ (that is, the depth of tree that is searched) more strictly, in order to
+ restrict the maximum amount of heap memory that is used. The value of
+ MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it
+ must be less than the value of MATCH_LIMIT. The default is to use the same
+ value as MATCH_LIMIT. There is a runtime method for setting a different
+ limit. */
+#ifndef MATCH_LIMIT_DEPTH
+#define MATCH_LIMIT_DEPTH MATCH_LIMIT
#endif
/* This limit is parameterized just in case anybody ever wants to change it.
@@ -196,8 +194,8 @@ sure both macros are undefined; an emulation function will then be used. */
/* The value of NEWLINE_DEFAULT determines the default newline character
sequence. PCRE2 client programs can override this by selecting other values
- at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), and 5
- (ANYCRLF). */
+ at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5
+ (ANYCRLF), and 6 (NUL). */
#ifndef NEWLINE_DEFAULT
#define NEWLINE_DEFAULT 2
#endif
@@ -212,7 +210,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_NAME "PCRE2"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "PCRE2 10.23"
+#define PACKAGE_STRING "PCRE2 10.31"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "pcre2"
@@ -221,7 +219,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "10.23"
+#define PACKAGE_VERSION "10.31"
/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested
parentheses (of any kind) in a pattern. This limits the amount of system
@@ -269,6 +267,11 @@ sure both macros are undefined; an emulation function will then be used. */
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
+/* Define to any non-zero number to enable support for SELinux compatible
+ executable memory allocator in JIT. Note that this will have no effect
+ unless SUPPORT_JIT is also defined. */
+/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */
+
/* Define to 1 if you have the ANSI C header files. */
/* #undef STDC_HEADERS */
@@ -336,7 +339,7 @@ sure both macros are undefined; an emulation function will then be used. */
#endif
/* Version number of package */
-#define VERSION "10.23"
+#define VERSION "10.31"
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
diff --git a/thirdparty/pcre2/src/pcre2.h b/thirdparty/pcre2/src/pcre2.h
index 86503208e8..fffcc307d0 100644
--- a/thirdparty/pcre2/src/pcre2.h
+++ b/thirdparty/pcre2/src/pcre2.h
@@ -5,7 +5,7 @@
/* This is the public header file for the PCRE library, second API, to be
#included by applications that call PCRE2 functions.
- Copyright (c) 2016 University of Cambridge
+ Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE.
/* The current PCRE version information. */
#define PCRE2_MAJOR 10
-#define PCRE2_MINOR 23
+#define PCRE2_MINOR 31
#define PCRE2_PRERELEASE
-#define PCRE2_DATE 2017-02-14
+#define PCRE2_DATE 2018-02-12
/* When an application links to a PCRE DLL in Windows, the symbols that are
imported have to be identified as such. When building PCRE2, the appropriate
@@ -101,6 +101,7 @@ others can be added next to them */
#define PCRE2_ANCHORED 0x80000000u
#define PCRE2_NO_UTF_CHECK 0x40000000u
+#define PCRE2_ENDANCHORED 0x20000000u
/* The following option bits can be passed only to pcre2_compile(). However,
they may affect compilation, JIT compilation, and/or interpretive execution.
@@ -136,6 +137,15 @@ D is inspected during pcre2_dfa_match() execution
#define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */
#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */
#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */
+#define PCRE2_EXTENDED_MORE 0x01000000u /* C */
+#define PCRE2_LITERAL 0x02000000u /* C */
+
+/* An additional compile options word is available in the compile context. */
+
+#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */
+#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */
+#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */
+#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */
/* These are for pcre2_jit_compile(). */
@@ -174,6 +184,16 @@ ignored for pcre2_jit_match(). */
#define PCRE2_NO_JIT 0x00002000u
+/* Options for pcre2_pattern_convert(). */
+
+#define PCRE2_CONVERT_UTF 0x00000001u
+#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u
+#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u
+#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u
+#define PCRE2_CONVERT_GLOB 0x00000010u
+#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u
+#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u
+
/* Newline and \R settings, for use in compile contexts. The newline values
must be kept in step with values set in config.h and both sets must all be
greater than zero. */
@@ -183,11 +203,109 @@ greater than zero. */
#define PCRE2_NEWLINE_CRLF 3
#define PCRE2_NEWLINE_ANY 4
#define PCRE2_NEWLINE_ANYCRLF 5
+#define PCRE2_NEWLINE_NUL 6
#define PCRE2_BSR_UNICODE 1
#define PCRE2_BSR_ANYCRLF 2
-/* Error codes: no match and partial match are "expected" errors. */
+/* Error codes for pcre2_compile(). Some of these are also used by
+pcre2_pattern_convert(). */
+
+#define PCRE2_ERROR_END_BACKSLASH 101
+#define PCRE2_ERROR_END_BACKSLASH_C 102
+#define PCRE2_ERROR_UNKNOWN_ESCAPE 103
+#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104
+#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105
+#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107
+#define PCRE2_ERROR_CLASS_RANGE_ORDER 108
+#define PCRE2_ERROR_QUANTIFIER_INVALID 109
+#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110
+#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111
+#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112
+#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113
+#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114
+#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115
+#define PCRE2_ERROR_NULL_PATTERN 116
+#define PCRE2_ERROR_BAD_OPTIONS 117
+#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118
+#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119
+#define PCRE2_ERROR_PATTERN_TOO_LARGE 120
+#define PCRE2_ERROR_HEAP_FAILED 121
+#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122
+#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123
+#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124
+#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125
+#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126
+#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127
+#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128
+#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129
+#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130
+#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131
+#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132
+#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133
+#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134
+#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135
+#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136
+#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137
+#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138
+#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140
+#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141
+#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142
+#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143
+#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144
+#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145
+#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146
+#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147
+#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148
+#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149
+#define PCRE2_ERROR_CLASS_INVALID_RANGE 150
+#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151
+#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152
+#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153
+#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154
+#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155
+#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156
+#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157
+#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158
+#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159
+#define PCRE2_ERROR_VERB_UNKNOWN 160
+#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161
+#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162
+#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163
+#define PCRE2_ERROR_INVALID_OCTAL 164
+#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165
+#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166
+#define PCRE2_ERROR_INVALID_HEXADECIMAL 167
+#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168
+#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170
+#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171
+#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172
+#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173
+#define PCRE2_ERROR_UTF_IS_DISABLED 174
+#define PCRE2_ERROR_UCP_IS_DISABLED 175
+#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176
+#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177
+#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178
+#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180
+#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181
+#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182
+#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183
+#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184
+#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185
+#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186
+#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187
+#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188
+#define PCRE2_ERROR_INTERNAL_BAD_CODE 189
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190
+#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191
+#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192
+
+
+/* "Expected" matching error codes: no match and partial match. */
#define PCRE2_ERROR_NOMATCH (-1)
#define PCRE2_ERROR_PARTIAL (-2)
@@ -227,10 +345,10 @@ greater than zero. */
#define PCRE2_ERROR_UTF32_ERR1 (-27)
#define PCRE2_ERROR_UTF32_ERR2 (-28)
-/* Error codes for pcre2[_dfa]_match(), substring extraction functions, context
-functions, and serializing functions. They are in numerical order. Originally
-they were in alphabetical order too, but now that PCRE2 is released, the
-numbers must not be changed. */
+/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction
+functions, context functions, and serializing functions. They are in numerical
+order. Originally they were in alphabetical order too, but now that PCRE2 is
+released, the numbers must not be changed. */
#define PCRE2_ERROR_BADDATA (-29)
#define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */
@@ -256,7 +374,8 @@ numbers must not be changed. */
#define PCRE2_ERROR_NOUNIQUESUBSTRING (-50)
#define PCRE2_ERROR_NULL (-51)
#define PCRE2_ERROR_RECURSELOOP (-52)
-#define PCRE2_ERROR_RECURSIONLIMIT (-53)
+#define PCRE2_ERROR_DEPTHLIMIT (-53)
+#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */
#define PCRE2_ERROR_UNAVAILABLE (-54)
#define PCRE2_ERROR_UNSET (-55)
#define PCRE2_ERROR_BADOFFSETLIMIT (-56)
@@ -266,6 +385,9 @@ numbers must not be changed. */
#define PCRE2_ERROR_BADSUBSPATTERN (-60)
#define PCRE2_ERROR_TOOMANYREPLACE (-61)
#define PCRE2_ERROR_BADSERIALIZEDDATA (-62)
+#define PCRE2_ERROR_HEAPLIMIT (-63)
+#define PCRE2_ERROR_CONVERT_SYNTAX (-64)
+
/* Request types for pcre2_pattern_info() */
@@ -290,9 +412,13 @@ numbers must not be changed. */
#define PCRE2_INFO_NAMEENTRYSIZE 18
#define PCRE2_INFO_NAMETABLE 19
#define PCRE2_INFO_NEWLINE 20
-#define PCRE2_INFO_RECURSIONLIMIT 21
+#define PCRE2_INFO_DEPTHLIMIT 21
+#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */
#define PCRE2_INFO_SIZE 22
#define PCRE2_INFO_HASBACKSLASHC 23
+#define PCRE2_INFO_FRAMESIZE 24
+#define PCRE2_INFO_HEAPLIMIT 25
+#define PCRE2_INFO_EXTRAOPTIONS 26
/* Request types for pcre2_config(). */
@@ -303,11 +429,16 @@ numbers must not be changed. */
#define PCRE2_CONFIG_MATCHLIMIT 4
#define PCRE2_CONFIG_NEWLINE 5
#define PCRE2_CONFIG_PARENSLIMIT 6
-#define PCRE2_CONFIG_RECURSIONLIMIT 7
-#define PCRE2_CONFIG_STACKRECURSE 8
+#define PCRE2_CONFIG_DEPTHLIMIT 7
+#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */
+#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */
#define PCRE2_CONFIG_UNICODE 9
#define PCRE2_CONFIG_UNICODE_VERSION 10
#define PCRE2_CONFIG_VERSION 11
+#define PCRE2_CONFIG_HEAPLIMIT 12
+#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13
+#define PCRE2_CONFIG_COMPILED_WIDTHS 14
+
/* Types for code units in patterns and subject strings. */
@@ -342,6 +473,9 @@ typedef struct pcre2_real_compile_context pcre2_compile_context; \
struct pcre2_real_match_context; \
typedef struct pcre2_real_match_context pcre2_match_context; \
\
+struct pcre2_real_convert_context; \
+typedef struct pcre2_real_convert_context pcre2_convert_context; \
+\
struct pcre2_real_code; \
typedef struct pcre2_real_code pcre2_code; \
\
@@ -360,6 +494,11 @@ without changing the API of the function, thereby allowing old clients to work
without modification. Define the generic version in a macro; the width-specific
versions are generated from this macro below. */
+/* Flags for the callout_flags field. These are cleared after a callout. */
+
+#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */
+#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */
+
#define PCRE2_STRUCTURE_LIST \
typedef struct pcre2_callout_block { \
uint32_t version; /* Identifies version of block */ \
@@ -379,6 +518,8 @@ typedef struct pcre2_callout_block { \
PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \
PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \
PCRE2_SPTR callout_string; /* String compiled into pattern */ \
+ /* ------------------- Added for Version 2 -------------------------- */ \
+ uint32_t callout_flags; /* See above for list */ \
/* ------------------------------------------------------------------ */ \
} pcre2_callout_block; \
\
@@ -426,6 +567,8 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_character_tables(pcre2_compile_context *, const unsigned char *); \
PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \
PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_newline(pcre2_compile_context *, uint32_t); \
@@ -446,6 +589,10 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_callout(pcre2_match_context *, \
int (*)(pcre2_callout_block *, void *), void *); \
PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_match_limit(pcre2_match_context *, uint32_t); \
PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \
@@ -455,6 +602,18 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
pcre2_set_recursion_memory_management(pcre2_match_context *, \
void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *);
+#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \
+ *pcre2_convert_context_copy(pcre2_convert_context *); \
+PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \
+ *pcre2_convert_context_create(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_free(pcre2_convert_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_separator(pcre2_convert_context *, uint32_t);
+
/* Functions concerned with compiling a pattern to PCRE internal code. */
@@ -561,6 +720,16 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *);
+/* Functions for converting pattern source strings. */
+
+#define PCRE2_CONVERT_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \
+ PCRE2_SIZE *, pcre2_convert_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_converted_pattern_free(PCRE2_UCHAR *);
+
+
/* Functions for JIT processing */
#define PCRE2_JIT_FUNCTIONS \
@@ -612,6 +781,7 @@ pcre2_compile are called by application code. */
#define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_)
#define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_)
#define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_)
+#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_)
#define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_)
#define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_)
#define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_)
@@ -623,6 +793,7 @@ pcre2_compile are called by application code. */
#define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_)
#define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_)
#define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_)
+#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_)
#define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_)
#define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_)
@@ -638,6 +809,10 @@ pcre2_compile are called by application code. */
#define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_)
#define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_)
#define pcre2_config PCRE2_SUFFIX(pcre2_config_)
+#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_)
+#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_)
+#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_)
+#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_)
#define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_)
#define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_)
#define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_)
@@ -661,6 +836,7 @@ pcre2_compile are called by application code. */
#define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_)
#define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_)
#define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_)
+#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_)
#define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_)
#define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_)
#define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_)
@@ -669,14 +845,17 @@ pcre2_compile are called by application code. */
#define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_)
#define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_)
#define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_)
+#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_)
#define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_)
+#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_)
+#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_)
+#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_)
+#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_)
#define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_)
#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_)
#define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_)
#define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_)
#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_)
-#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_)
-#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_)
#define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_)
#define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_)
#define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_)
@@ -690,6 +869,11 @@ pcre2_compile are called by application code. */
#define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_)
#define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_)
+/* Keep this old function name for backwards compatibility */
+#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_)
+
+/* Keep this obsolete function for backwards compatibility: it is now a noop. */
+#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_)
/* Now generate all three sets of width-specific structures and function
prototypes. */
@@ -700,6 +884,8 @@ PCRE2_STRUCTURE_LIST \
PCRE2_GENERAL_INFO_FUNCTIONS \
PCRE2_GENERAL_CONTEXT_FUNCTIONS \
PCRE2_COMPILE_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_FUNCTIONS \
PCRE2_MATCH_CONTEXT_FUNCTIONS \
PCRE2_COMPILE_FUNCTIONS \
PCRE2_PATTERN_INFO_FUNCTIONS \
@@ -729,6 +915,7 @@ PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
#undef PCRE2_GENERAL_INFO_FUNCTIONS
#undef PCRE2_GENERAL_CONTEXT_FUNCTIONS
#undef PCRE2_COMPILE_CONTEXT_FUNCTIONS
+#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS
#undef PCRE2_MATCH_CONTEXT_FUNCTIONS
#undef PCRE2_COMPILE_FUNCTIONS
#undef PCRE2_PATTERN_INFO_FUNCTIONS
diff --git a/thirdparty/pcre2/src/pcre2_auto_possess.c b/thirdparty/pcre2/src/pcre2_auto_possess.c
index 64ec6dfbbc..23275a2e39 100644
--- a/thirdparty/pcre2/src/pcre2_auto_possess.c
+++ b/thirdparty/pcre2/src/pcre2_auto_possess.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -558,50 +558,74 @@ for(;;)
continue;
}
+ /* At the end of a branch, skip to the end of the group. */
+
if (c == OP_ALT)
{
do code += GET(code, 1); while (*code == OP_ALT);
c = *code;
}
+ /* Inspect the next opcode. */
+
switch(c)
{
- case OP_END:
- case OP_KETRPOS:
- /* TRUE only in greedy case. The non-greedy case could be replaced by
- an OP_EXACT, but it is probably not worth it. (And note that OP_EXACT
- uses more memory, which we cannot get at this stage.) */
+ /* We can always possessify a greedy iterator at the end of the pattern,
+ which is reached after skipping over the final OP_KET. A non-greedy
+ iterator must never be possessified. */
+ case OP_END:
return base_list[1] != 0;
+ /* When an iterator is at the end of certain kinds of group we can inspect
+ what follows the group by skipping over the closing ket. Note that this
+ does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given
+ iteration is variable (could be another iteration or could be the next
+ item). As these two opcodes are not listed in the next switch, they will
+ end up as the next code to inspect, and return FALSE by virtue of being
+ unsupported. */
+
case OP_KET:
- /* If the bracket is capturing, and referenced by an OP_RECURSE, or
- it is an atomic sub-pattern (assert, once, etc.) the non-greedy case
- cannot be converted to a possessive form. */
+ case OP_KETRPOS:
+ /* The non-greedy case cannot be converted to a possessive form. */
if (base_list[1] == 0) return FALSE;
+ /* If the bracket is capturing it might be referenced by an OP_RECURSE
+ so its last iterator can never be possessified if the pattern contains
+ recursions. (This could be improved by keeping a list of group numbers that
+ are called by recursion.) */
+
switch(*(code - GET(code, 1)))
{
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ if (cb->had_recurse) return FALSE;
+ break;
+
+ /* Atomic sub-patterns and assertions can always auto-possessify their
+ last iterator. However, if the group was entered as a result of checking
+ a previous iterator, this is not possible. */
+
case OP_ASSERT:
case OP_ASSERT_NOT:
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ONCE:
- case OP_ONCE_NC:
-
- /* Atomic sub-patterns and assertions can always auto-possessify their
- last iterator. However, if the group was entered as a result of checking
- a previous iterator, this is not possible. */
return !entered_a_group;
}
+ /* Skip over the bracket and inspect what comes next. */
+
code += PRIV(OP_lengths)[c];
continue;
+ /* Handle cases where the next item is a group. */
+
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_CBRA:
next_code = code + GET(code, 1);
@@ -625,8 +649,8 @@ for(;;)
case OP_BRAMINZERO:
next_code = code + 1;
- if (*next_code != OP_BRA && *next_code != OP_CBRA
- && *next_code != OP_ONCE && *next_code != OP_ONCE_NC) return FALSE;
+ if (*next_code != OP_BRA && *next_code != OP_CBRA &&
+ *next_code != OP_ONCE) return FALSE;
do next_code += GET(next_code, 1); while (*next_code == OP_ALT);
@@ -639,11 +663,15 @@ for(;;)
code += PRIV(OP_lengths)[c];
continue;
+ /* The next opcode does not need special handling; fall through and use it
+ to see if the base can be possessified. */
+
default:
break;
}
- /* Check for a supported opcode, and load its properties. */
+ /* We now have the next appropriate opcode to compare with the base. Check
+ for a supported opcode, and load its properties. */
code = get_chr_property_list(code, utf, cb->fcc, list);
if (code == NULL) return FALSE; /* Unsupported */
@@ -1077,7 +1105,7 @@ for (;;)
{
c = *code;
- if (c > OP_TABLE_LENGTH) return -1; /* Something gone wrong */
+ if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */
if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)
{
diff --git a/thirdparty/pcre2/src/pcre2_compile.c b/thirdparty/pcre2/src/pcre2_compile.c
index 6d98a68caa..87530fb584 100644
--- a/thirdparty/pcre2/src/pcre2_compile.c
+++ b/thirdparty/pcre2/src/pcre2_compile.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -160,7 +160,7 @@ the length of compiled items varies with this.
In the real compile phase, this workspace is not currently used. */
-#define COMPILE_WORK_SIZE (2048*LINK_SIZE) /* Size in code units */
+#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */
#define C16_WORK_SIZE \
((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t))
@@ -690,17 +690,29 @@ static int posix_substitutes[] = {
#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t)))
#endif /* SUPPORT_UNICODE */
-/* Masks for checking option settings. */
+/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset
+are allowed. */
+
+#define PUBLIC_LITERAL_COMPILE_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \
+ PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_NO_START_OPTIMIZE| \
+ PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF)
#define PUBLIC_COMPILE_OPTIONS \
- (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \
- PCRE2_ALT_VERBNAMES|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY| \
- PCRE2_DOTALL|PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_FIRSTLINE| \
- PCRE2_MATCH_UNSET_BACKREF|PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C| \
- PCRE2_NEVER_UCP|PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE| \
- PCRE2_NO_AUTO_POSSESS|PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE| \
- PCRE2_NO_UTF_CHECK|PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_USE_OFFSET_LIMIT| \
- PCRE2_UTF)
+ (PUBLIC_LITERAL_COMPILE_OPTIONS| \
+ PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \
+ PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \
+ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \
+ PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \
+ PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \
+ PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY)
+
+#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \
+ (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD)
+
+#define PUBLIC_COMPILE_EXTRA_OPTIONS \
+ (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \
+ PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL)
/* Compile time error code numbers. They are given names so that they can more
easily be tracked. When a new number is added, the tables called eint1 and
@@ -716,7 +728,8 @@ enum { ERR0 = COMPILE_ERROR_BASE,
ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60,
ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70,
ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80,
- ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90 };
+ ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90,
+ ERR91, ERR92};
/* This is a table of start-of-pattern options such as (*UTF) and settings such
as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward
@@ -727,8 +740,9 @@ enum { PSO_OPT, /* Value is an option bit */
PSO_FLG, /* Value is a flag bit */
PSO_NL, /* Value is a newline type */
PSO_BSR, /* Value is a \R type */
+ PSO_LIMH, /* Read integer value for heap limit */
PSO_LIMM, /* Read integer value for match limit */
- PSO_LIMR }; /* Read integer value for recursion limit */
+ PSO_LIMD }; /* Read integer value for depth limit */
typedef struct pso {
const uint8_t *name;
@@ -749,12 +763,15 @@ static pso pso_list[] = {
{ (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR },
{ (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT },
{ (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE },
+ { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 },
{ (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 },
- { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMR, 0 },
+ { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 },
+ { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 },
{ (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR },
{ (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF },
{ (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF },
{ (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY },
+ { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL },
{ (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF },
{ (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF },
{ (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE }
@@ -1470,7 +1487,10 @@ else
if (utf)
{
if (c > 0x10ffffU) *errorcodeptr = ERR77;
- else if (c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73;
+ else
+ if (c >= 0xd800 && c <= 0xdfff &&
+ (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ *errorcodeptr = ERR73;
}
else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77;
}
@@ -1604,7 +1624,7 @@ else
if (c >= CHAR_8) break;
- /* Fall through with a digit less than 8 */
+ /* Fall through */
/* \0 always starts an octal number, but we may drop through to here with a
larger first octal digit. The original code used just to take the least
@@ -1659,7 +1679,8 @@ else
}
else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
{
- if (utf && c >= 0xd800 && c <= 0xdfff)
+ if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL ||
+ (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0))
{
ptr--;
*errorcodeptr = ERR73;
@@ -1728,7 +1749,8 @@ else
}
else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
{
- if (utf && c >= 0xd800 && c <= 0xdfff)
+ if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL ||
+ (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0))
{
ptr--;
*errorcodeptr = ERR73;
@@ -1901,7 +1923,7 @@ if (c == CHAR_LEFT_CURLY_BRACKET)
{
if (ptr >= cb->end_pattern) goto ERROR_RETURN;
c = *ptr++;
- if (c == CHAR_NULL) goto ERROR_RETURN;
+ if (c == CHAR_NUL) goto ERROR_RETURN;
if (c == CHAR_RIGHT_CURLY_BRACKET) break;
name[i] = c;
}
@@ -2159,7 +2181,7 @@ the parsed pattern.
Arguments:
ptr current pattern pointer
pcalloutptr points to a pointer to previous callout, or NULL
- options the compiling options
+ auto_callout TRUE if auto_callouts are enabled
parsed_pattern the parsed pattern pointer
cb compile block
@@ -2167,15 +2189,15 @@ Returns: possibly updated parsed_pattern pointer.
*/
static uint32_t *
-manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, uint32_t options,
+manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout,
uint32_t *parsed_pattern, compile_block *cb)
{
uint32_t *previous_callout = *pcalloutptr;
-if (previous_callout != NULL) previous_callout[2] = ptr - cb->start_pattern -
- (PCRE2_SIZE)previous_callout[1];
+if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr -
+ cb->start_pattern - (PCRE2_SIZE)previous_callout[1]);
-if ((options & PCRE2_AUTO_CALLOUT) == 0) previous_callout = NULL; else
+if (!auto_callout) previous_callout = NULL; else
{
if (previous_callout == NULL ||
previous_callout != parsed_pattern - 4 ||
@@ -2223,12 +2245,17 @@ typedef struct nest_save {
uint16_t reset_group;
uint16_t max_group;
uint16_t flags;
+ uint32_t options;
} nest_save;
-#define NSF_RESET 0x0001u
-#define NSF_EXTENDED 0x0002u
-#define NSF_DUPNAMES 0x0004u
-#define NSF_CONDASSERT 0x0008u
+#define NSF_RESET 0x0001u
+#define NSF_CONDASSERT 0x0002u
+
+/* Of the options that are changeable within the pattern, these are tracked
+during parsing. The rest are used from META_OPTIONS items when compiling. */
+
+#define PARSE_TRACKED_OPTIONS \
+ (PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_NO_AUTO_CAPTURE)
/* States used for analyzing ranges in character classes. The two OK values
must be last. */
@@ -2272,15 +2299,57 @@ int i;
BOOL inescq = FALSE;
BOOL inverbname = FALSE;
BOOL utf = (options & PCRE2_UTF) != 0;
+BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0;
BOOL isdupname;
BOOL negate_class;
BOOL okquantifier = FALSE;
+PCRE2_SPTR thisptr;
PCRE2_SPTR name;
PCRE2_SPTR ptrend = cb->end_pattern;
PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */
named_group *ng;
-nest_save *top_nest = NULL;
-nest_save *end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size);
+nest_save *top_nest, *end_nests;
+
+/* Insert leading items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_CIRCUMFLEX;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+
+/* If the pattern is actually a literal string, process it separately to avoid
+cluttering up the main loop. */
+
+if ((options & PCRE2_LITERAL) != 0)
+ {
+ while (ptr < ptrend)
+ {
+ if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+ thisptr = ptr;
+ GETCHARINCTEST(c, ptr);
+ if (auto_callout)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ goto PARSED_END;
+ }
+
+/* Process a real regex which may contain meta-characters. */
+
+top_nest = NULL;
+end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size);
/* The size of the nest_save structure might not be a factor of the size of the
workspace. Therefore we must round down end_nests so as to correctly avoid
@@ -2289,9 +2358,11 @@ creating a nest_save that spans the end of the workspace. */
end_nests = (nest_save *)((char *)end_nests -
((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save)));
-/* Now scan the pattern */
+/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */
-*has_lookbehind = FALSE;
+if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED;
+
+/* Now scan the pattern */
while (ptr < ptrend)
{
@@ -2302,7 +2373,6 @@ while (ptr < ptrend)
uint32_t prev_meta_quantifier;
BOOL prev_okquantifier;
PCRE2_SPTR tempptr;
- PCRE2_SPTR thisptr;
PCRE2_SIZE offset;
if (parsed_pattern >= parsed_pattern_end)
@@ -2314,7 +2384,7 @@ while (ptr < ptrend)
if (nest_depth > cb->cx->parens_nest_limit)
{
errorcode = ERR19;
- goto FAILED;
+ goto FAILED; /* Parentheses too deeply nested */
}
/* Get next input character, save its position for callout handling. */
@@ -2341,8 +2411,8 @@ while (ptr < ptrend)
goto FAILED;
}
if (!inverbname && after_manual_callout-- <= 0)
- parsed_pattern = manage_callouts(thisptr, &previous_callout, options,
- parsed_pattern, cb);
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
PARSED_LITERAL(c, parsed_pattern);
meta_quantifier = 0;
}
@@ -2487,7 +2557,7 @@ while (ptr < ptrend)
!read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode))))
{
if (after_manual_callout-- <= 0)
- parsed_pattern = manage_callouts(thisptr, &previous_callout, options,
+ parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout,
parsed_pattern, cb);
}
@@ -2571,11 +2641,23 @@ while (ptr < ptrend)
/* ---- Escape sequence ---- */
case CHAR_BACKSLASH:
+ tempptr = ptr;
escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
FALSE, cb);
- if (errorcode != 0) goto FAILED;
+ if (errorcode != 0)
+ {
+ ESCAPE_FAILED:
+ if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
- /* The escape was a data character. */
+ /* The escape was a data escape or literal character. */
if (escape == 0)
{
@@ -2627,12 +2709,12 @@ while (ptr < ptrend)
case ESC_C:
#ifdef NEVER_BACKSLASH_C
errorcode = ERR85;
- goto FAILED;
+ goto ESCAPE_FAILED;
#else
if ((options & PCRE2_NEVER_BACKSLASH_C) != 0)
{
errorcode = ERR83;
- goto FAILED;
+ goto ESCAPE_FAILED;
}
#endif
okquantifier = TRUE;
@@ -2642,7 +2724,7 @@ while (ptr < ptrend)
case ESC_X:
#ifndef SUPPORT_UNICODE
errorcode = ERR45; /* Supported only with Unicode support */
- goto FAILED;
+ goto ESCAPE_FAILED;
#endif
case ESC_H:
case ESC_h:
@@ -2707,7 +2789,7 @@ while (ptr < ptrend)
BOOL negated;
uint16_t ptype = 0, pdata = 0;
if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))
- goto FAILED;
+ goto ESCAPE_FAILED;
if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;
*parsed_pattern++ = META_ESCAPE + escape;
*parsed_pattern++ = (ptype << 16) | pdata;
@@ -2715,7 +2797,7 @@ while (ptr < ptrend)
}
#else
errorcode = ERR45;
- goto FAILED;
+ goto ESCAPE_FAILED;
#endif
break; /* End \P and \p */
@@ -2731,7 +2813,7 @@ while (ptr < ptrend)
*ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE))
{
errorcode = (escape == ESC_g)? ERR57 : ERR69;
- goto FAILED;
+ goto ESCAPE_FAILED;
}
terminator = (*ptr == CHAR_LESS_THAN_SIGN)?
CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)?
@@ -2749,18 +2831,18 @@ while (ptr < ptrend)
if (p >= ptrend || *p != terminator)
{
errorcode = ERR57;
- goto FAILED;
+ goto ESCAPE_FAILED;
}
ptr = p;
goto SET_RECURSION;
}
- if (errorcode != 0) goto FAILED;
+ if (errorcode != 0) goto ESCAPE_FAILED;
}
/* Not a numerical recursion */
if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen,
- &errorcode, cb)) goto FAILED;
+ &errorcode, cb)) goto ESCAPE_FAILED;
/* \k and \g when used with braces are back references, whereas \g used
with quotes or angle brackets is a recursion */
@@ -2772,7 +2854,7 @@ while (ptr < ptrend)
PUTOFFSET(offset, parsed_pattern);
okquantifier = TRUE;
- break;
+ break; /* End special escape processing */
}
break; /* End escape sequence processing */
@@ -2904,7 +2986,8 @@ while (ptr < ptrend)
/* Process a regular character class. If the first character is '^', set
the negation flag. If the first few characters (either before or after ^)
- are \Q\E or \E we skip them too. This makes for compatibility with Perl. */
+ are \Q\E or \E or space or tab in extended-more mode, we skip them too.
+ This makes for compatibility with Perl. */
negate_class = FALSE;
while (ptr < ptrend)
@@ -2919,6 +3002,9 @@ while (ptr < ptrend)
else
break;
}
+ else if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */
+ continue;
else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT)
negate_class = TRUE;
else break;
@@ -2966,6 +3052,12 @@ while (ptr < ptrend)
goto CLASS_LITERAL;
}
+ /* Skip over space and tab (only) in extended-more mode. */
+
+ if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT))
+ goto CLASS_CONTINUE;
+
/* Handle POSIX class names. Perl allows a negation extension of the
form [:^name:]. A square bracket that doesn't match the syntax is
treated as a literal. We also recognize the POSIX constructions
@@ -3013,21 +3105,23 @@ while (ptr < ptrend)
ptr = tempptr + 2;
/* Perl treats a hyphen after a POSIX class as a literal, not the
- start of a range. However, it gives a warning in its warning mode. PCRE
- does not have a warning mode, so we give an error, because this is
- likely an error on the user's part. */
+ start of a range. However, it gives a warning in its warning mode
+ unless the hyphen is the last character in the class. PCRE does not
+ have a warning mode, so we give an error, because this is likely an
+ error on the user's part. */
- if (ptr < ptrend && *ptr == CHAR_MINUS)
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
{
errorcode = ERR50;
goto FAILED;
}
- /* Set "a hyphen is not the start of a range" just in case the POSIX
- class is followed by \E or \Q\E (possibly repeated - fuzzers do that
- kind of thing) and *then* a hyphen. This causes that hyphen to be
- treated as a literal. I don't think it's worth setting up special
- apparatus to do otherwise. */
+ /* Set "a hyphen is not the start of a range" for the -] case, and also
+ in case the POSIX class is followed by \E or \Q\E (possibly repeated -
+ fuzzers do that kind of thing) and *then* a hyphen. This causes that
+ hyphen to be treated as a literal. I don't think it's worth setting up
+ special apparatus to do otherwise. */
class_range_state = RANGE_NO;
@@ -3109,10 +3203,23 @@ while (ptr < ptrend)
else
{
+ tempptr = ptr;
escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode,
options, TRUE, cb);
- if (errorcode != 0) goto FAILED;
+ if (errorcode != 0)
+ {
+ CLASS_ESCAPE_FAILED:
+ if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
+
if (escape == 0) /* Escaped character code point is in c */
{
char_is_literal = FALSE;
@@ -3146,7 +3253,7 @@ while (ptr < ptrend)
if (class_range_state == RANGE_STARTED)
{
errorcode = ERR50;
- goto FAILED;
+ goto CLASS_ESCAPE_FAILED;
}
/* Of the remaining escapes, only those that define characters are
@@ -3157,7 +3264,7 @@ while (ptr < ptrend)
{
case ESC_N:
errorcode = ERR71; /* Not supported in a class */
- goto FAILED;
+ goto CLASS_ESCAPE_FAILED;
case ESC_H:
case ESC_h:
@@ -3220,13 +3327,24 @@ while (ptr < ptrend)
}
#else
errorcode = ERR45;
- goto FAILED;
+ goto CLASS_ESCAPE_FAILED;
#endif
break; /* End \P and \p */
default: /* All others are not allowed in a class */
errorcode = ERR7;
- goto FAILED_BACK;
+ ptr--;
+ goto CLASS_ESCAPE_FAILED;
+ }
+
+ /* Perl gives a warning unless a following hyphen is the last character
+ in the class. PCRE throws an error. */
+
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ errorcode = ERR50;
+ goto FAILED;
}
}
@@ -3384,8 +3502,7 @@ while (ptr < ptrend)
}
top_nest->nest_depth = nest_depth;
top_nest->flags = 0;
- if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED;
- if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
/* Start of non-capturing group that resets the capture count for each
branch. */
@@ -3400,9 +3517,7 @@ while (ptr < ptrend)
ptr++;
}
- /* Scan for options imsxJU. We need to keep track of (?x) and (?J) for
- use while scanning. The other options are used during the compiling
- phases. */
+ /* Scan for options imnsxJU to be set or unset. */
else
{
@@ -3425,16 +3540,36 @@ while (ptr < ptrend)
case CHAR_i: *optset |= PCRE2_CASELESS; break;
case CHAR_m: *optset |= PCRE2_MULTILINE; break;
+ case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break;
case CHAR_s: *optset |= PCRE2_DOTALL; break;
- case CHAR_x: *optset |= PCRE2_EXTENDED; break;
case CHAR_U: *optset |= PCRE2_UNGREEDY; break;
+ /* If x appears twice it sets the extended extended option. */
+
+ case CHAR_x:
+ *optset |= PCRE2_EXTENDED;
+ if (ptr < ptrend && *ptr == CHAR_x)
+ {
+ *optset |= PCRE2_EXTENDED_MORE;
+ ptr++;
+ }
+ break;
+
default:
errorcode = ERR11;
ptr--; /* Correct the offset */
goto FAILED;
}
}
+
+ /* If we are setting extended without extended-more, ensure that any
+ existing extended-more gets unset. Also, unsetting extended must also
+ unset extended-more. */
+
+ if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED ||
+ (unset & PCRE2_EXTENDED) != 0)
+ unset |= PCRE2_EXTENDED_MORE;
+
options = (options | set) & (~unset);
/* If the options ended with ')' this is not the start of a nested
@@ -3671,7 +3806,7 @@ while (ptr < ptrend)
/* Remember the offset to the next item in the pattern, and set a default
length. This should get updated after the next item is read. */
- previous_callout[1] = ptr - cb->start_pattern;
+ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);
previous_callout[2] = 0;
break; /* End callout */
@@ -3913,8 +4048,7 @@ while (ptr < ptrend)
}
top_nest->nest_depth = nest_depth;
top_nest->flags = NSF_CONDASSERT;
- if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED;
- if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
}
break;
@@ -4035,20 +4169,17 @@ while (ptr < ptrend)
break;
/* End of group; reset the capture count to the maximum if we are in a (?|
- group and/or reset the extended and dupnames options. Disallow quantifier
- for a condition that is an assertion. */
+ group and/or reset the options that are tracked during parsing. Disallow
+ quantifier for a condition that is an assertion. */
case CHAR_RIGHT_PARENTHESIS:
okquantifier = TRUE;
if (top_nest != NULL && top_nest->nest_depth == nest_depth)
{
+ options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options;
if ((top_nest->flags & NSF_RESET) != 0 &&
top_nest->max_group > cb->bracount)
cb->bracount = top_nest->max_group;
- if ((top_nest->flags & NSF_EXTENDED) != 0) options |= PCRE2_EXTENDED;
- else options &= ~PCRE2_EXTENDED;
- if ((top_nest->flags & NSF_DUPNAMES) != 0) options |= PCRE2_DUPNAMES;
- else options &= ~PCRE2_DUPNAMES;
if ((top_nest->flags & NSF_CONDASSERT) != 0)
okquantifier = FALSE;
if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL;
@@ -4075,9 +4206,24 @@ if (inverbname && ptr >= ptrend)
/* Manage callout for the final item */
-parsed_pattern = manage_callouts(ptr, &previous_callout, options,
+PARSED_END:
+parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout,
parsed_pattern, cb);
+/* Insert trailing items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_DOLLAR;
+ }
+else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ }
+
/* Terminate the parsed pattern, then return success if all groups are closed.
Otherwise we have unclosed parentheses. */
@@ -4086,6 +4232,7 @@ if (parsed_pattern >= parsed_pattern_end)
errorcode = ERR63; /* Internal error (parsed pattern overflow) */
goto FAILED;
}
+
*parsed_pattern = META_END;
if (nest_depth == 0) return 0;
@@ -4164,6 +4311,18 @@ for (;;)
code += GET(code, 1 + 2*LINK_SIZE);
break;
+ case OP_SKIPZERO:
+ code += 2 + GET(code, 2) + LINK_SIZE;
+ break;
+
+ case OP_COND:
+ case OP_SCOND:
+ if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */
+ code[GET(code, 1)] != OP_KET) /* More than one branch */
+ return code;
+ code += GET(code, 1) + 1 + LINK_SIZE;
+ break;
+
default:
return code;
}
@@ -4746,7 +4905,6 @@ for (;; pptr++)
int class_has_8bitchar;
int i;
uint32_t mclength;
- uint32_t templastcapture;
uint32_t skipunits;
uint32_t subreqcu, subfirstcu;
uint32_t groupnumber;
@@ -5198,6 +5356,10 @@ for (;; pptr++)
options & ~PCRE2_CASELESS, cb, PRIV(vspace_list));
break;
+ /* If Unicode is not supported, \P and \p are not allowed and are
+ faulted at parse time, so will never appear here. */
+
+#ifdef SUPPORT_UNICODE
case ESC_p:
case ESC_P:
{
@@ -5206,12 +5368,11 @@ for (;; pptr++)
*class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP;
*class_uchardata++ = ptype;
*class_uchardata++ = pdata;
-#ifdef SUPPORT_WIDE_CHARS
xclass_has_prop = TRUE;
-#endif
class_has_8bitchar--; /* Undo! */
}
break;
+#endif
}
goto CONTINUE_CLASS;
@@ -5438,14 +5599,17 @@ for (;; pptr++)
/* ===================================================================*/
/* Deal with (*VERB)s. */
- /* Check for open captures before ACCEPT and convert it to ASSERT_ACCEPT if
- in an assertion. In the first pass, just accumulate the length required;
+ /* Check for open captures before ACCEPT and close those that are within
+ the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an
+ assertion. In the first pass, just accumulate the length required;
otherwise hitting (*ACCEPT) inside many nested parentheses can cause
workspace overflow. Do not set firstcu after *ACCEPT. */
case META_ACCEPT:
cb->had_accept = TRUE;
- for (oc = cb->open_caps; oc != NULL; oc = oc->next)
+ for (oc = cb->open_caps;
+ oc != NULL && oc->assert_depth >= cb->assert_depth;
+ oc = oc->next)
{
if (lengthptr != NULL)
{
@@ -5753,7 +5917,6 @@ for (;; pptr++)
pptr++;
tempcode = code;
tempreqvary = cb->req_varyopt; /* Save value before group */
- templastcapture = cb->lastcapture; /* Save value before group */
length_prevgroup = 0; /* Initialize for pre-compile phase */
if ((group_return =
@@ -5783,12 +5946,6 @@ for (;; pptr++)
if (note_group_empty && bravalue != OP_COND && group_return > 0)
matched_char = TRUE;
- /* If that was an atomic group and there are no capturing groups within it,
- generate OP_ONCE_NC instead of OP_ONCE. */
-
- if (bravalue == OP_ONCE && cb->lastcapture <= templastcapture)
- *code = OP_ONCE_NC;
-
/* If we've just compiled an assertion, pop the assert depth. */
if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT)
@@ -6113,7 +6270,7 @@ for (;; pptr++)
}
else *callout_string++ = *pp++;
}
- *callout_string++ = CHAR_NULL;
+ *callout_string++ = CHAR_NUL;
/* Set the length of the entire item, the advance to its end. */
@@ -6209,24 +6366,6 @@ for (;; pptr++)
tempcode = previous;
op_previous = *previous;
- /* If previous was a recursion call, wrap it in atomic brackets so that
- previous becomes the atomic group. All recursions were so wrapped in the
- past, but it no longer happens for non-repeated recursions. In fact, the
- repeated ones could be re-implemented independently so as not to need this,
- but for the moment we rely on the code for repeating groups. */
-
- if (op_previous == OP_RECURSE)
- {
- memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE));
- op_previous = *previous = OP_ONCE;
- PUT(previous, 1, 2 + 2*LINK_SIZE);
- previous[2 + 2*LINK_SIZE] = OP_KET;
- PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE);
- code += 2 + 2 * LINK_SIZE;
- length_prevgroup = 3 + 3*LINK_SIZE;
- group_return = -1; /* Set "may match empty string" */
- }
-
/* Now handle repetition for the different types of item. */
switch (op_previous)
@@ -6311,6 +6450,77 @@ for (;; pptr++)
case OP_FAIL:
goto END_REPEAT;
+ /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets
+ because pcre2_match() could not handle backtracking into recursively
+ called groups. Now that this backtracking is available, we no longer need
+ to do this. However, we still need to replicate recursions as we do for
+ groups so as to have independent backtracking points. We can replicate
+ for the minimum number of repeats directly. For optional repeats we now
+ wrap the recursion in OP_BRA brackets and make use of the bracket
+ repetition. */
+
+ case OP_RECURSE:
+
+ /* Generate unwrapped repeats for a non-zero minimum, except when the
+ minimum is 1 and the maximum unlimited, because that can be handled with
+ OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the
+ minimum, we just need to generate the appropriate additional copies.
+ Otherwise we need to generate one more, to simulate the situation when
+ the minimum is zero. */
+
+ if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED))
+ {
+ int replicate = repeat_min;
+ if (repeat_min == repeat_max) replicate--;
+
+ /* In the pre-compile phase, we don't actually do the replication. We
+ just adjust the length as if we had. Do some paranoid checks for
+ potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
+ integer type when available, otherwise double. */
+
+ if (lengthptr != NULL)
+ {
+ PCRE2_SIZE delta = replicate*(1 + LINK_SIZE);
+ if ((INT64_OR_DOUBLE)replicate*
+ (INT64_OR_DOUBLE)(1 + LINK_SIZE) >
+ (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ else for (i = 0; i < replicate; i++)
+ {
+ memcpy(code, previous, CU2BYTES(1 + LINK_SIZE));
+ previous = code;
+ code += 1 + LINK_SIZE;
+ }
+
+ /* If the number of repeats is fixed, we are done. Otherwise, adjust
+ the counts and fall through. */
+
+ if (repeat_min == repeat_max) break;
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;
+ repeat_min = 0;
+ }
+
+ /* Wrap the recursion call in OP_BRA brackets. */
+
+ memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE));
+ op_previous = *previous = OP_BRA;
+ PUT(previous, 1, 2 + 2*LINK_SIZE);
+ previous[2 + 2*LINK_SIZE] = OP_KET;
+ PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE);
+ code += 2 + 2 * LINK_SIZE;
+ length_prevgroup = 3 + 3*LINK_SIZE;
+ group_return = -1; /* Set "may match empty string" */
+
+ /* Now treat as a repeated OP_BRA. */
+ /* Fall through */
+
/* If previous was a bracket group, we may have to replicate it in
certain cases. Note that at this point we can encounter only the "basic"
bracket opcodes such as BRA and CBRA, as this is the place where they get
@@ -6323,7 +6533,6 @@ for (;; pptr++)
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_CBRA:
case OP_COND:
@@ -6340,10 +6549,10 @@ for (;; pptr++)
previous[GET(previous, 1)] != OP_ALT)
goto END_REPEAT;
- /* There is no sense in actually repeating assertions. The only potential
- use of repetition is in cases when the assertion is optional. Therefore,
- if the minimum is greater than zero, just ignore the repeat. If the
- maximum is not zero or one, set it to 1. */
+ /* There is no sense in actually repeating assertions. The only
+ potential use of repetition is in cases when the assertion is optional.
+ Therefore, if the minimum is greater than zero, just ignore the repeat.
+ If the maximum is not zero or one, set it to 1. */
if (op_previous < OP_ONCE) /* Assertion */
{
@@ -6567,14 +6776,12 @@ for (;; pptr++)
/* Convert possessive ONCE brackets to non-capturing */
- if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) &&
- possessive_quantifier) *bracode = OP_BRA;
+ if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA;
/* For non-possessive ONCE brackets, all we need to do is to
set the KET. */
- if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC)
- *ketcode = OP_KETRMAX + repeat_type;
+ if (*bracode == OP_ONCE) *ketcode = OP_KETRMAX + repeat_type;
/* Handle non-ONCE brackets and possessive ONCEs (which have been
converted to non-capturing above). */
@@ -6928,7 +7135,7 @@ for (;; pptr++)
later. */
HANDLE_SINGLE_REFERENCE:
- if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE;
*code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF;
PUT2INC(code, 0, meta_arg);
@@ -7143,7 +7350,6 @@ for (;; pptr++)
if (mclength == 1 || req_caseopt == 0)
{
- firstcu = mcbuffer[0] | req_caseopt;
firstcu = mcbuffer[0];
firstcuflags = req_caseopt;
if (mclength != 1)
@@ -7280,6 +7486,7 @@ if (*code == OP_CBRA)
capitem.number = capnumber;
capitem.next = cb->open_caps;
capitem.flag = FALSE;
+ capitem.assert_depth = cb->assert_depth;
cb->open_caps = &capitem;
}
@@ -7568,7 +7775,7 @@ do {
/* Atomic groups */
- else if (op == OP_ONCE || op == OP_ONCE_NC)
+ else if (op == OP_ONCE)
{
if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert))
return FALSE;
@@ -7698,7 +7905,7 @@ do {
/* Atomic brackets */
- else if (op == OP_ONCE || op == OP_ONCE_NC)
+ else if (op == OP_ONCE)
{
if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert))
return FALSE;
@@ -7720,9 +7927,8 @@ do {
}
/* Check for explicit circumflex; anything else gives a FALSE result. Note
- in particular that this includes atomic brackets OP_ONCE and OP_ONCE_NC
- because the number of characters matched by .* cannot be adjusted inside
- them. */
+ in particular that this includes atomic brackets OP_ONCE because the number
+ of characters matched by .* cannot be adjusted inside them. */
else if (op != OP_CIRC && op != OP_CIRCM) return FALSE;
@@ -7900,13 +8106,13 @@ REQ_NONE in the flags.
Arguments:
code points to start of compiled pattern
flags points to the first code unit flags
- inassert TRUE if in an assertion
+ inassert non-zero if in an assertion
Returns: the fixed first code unit, or 0 with REQ_NONE in flags
*/
static uint32_t
-find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, BOOL inassert)
+find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, uint32_t inassert)
{
uint32_t c = 0;
int cflags = REQ_NONE;
@@ -7933,8 +8139,7 @@ do {
case OP_SCBRAPOS:
case OP_ASSERT:
case OP_ONCE:
- case OP_ONCE_NC:
- d = find_firstassertedcu(scode, &dflags, op == OP_ASSERT);
+ d = find_firstassertedcu(scode, &dflags, inassert + ((op==OP_ASSERT)?1:0));
if (dflags < 0)
return 0;
if (cflags < 0) { c = d; cflags = dflags; }
@@ -7949,7 +8154,7 @@ do {
case OP_PLUS:
case OP_MINPLUS:
case OP_POSPLUS:
- if (!inassert) return 0;
+ if (inassert == 0) return 0;
if (cflags < 0) { c = scode[1]; cflags = 0; }
else if (c != scode[1]) return 0;
break;
@@ -7962,7 +8167,7 @@ do {
case OP_PLUSI:
case OP_MINPLUSI:
case OP_POSPLUSI:
- if (!inassert) return 0;
+ if (inassert == 0) return 0;
if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; }
else if (c != scode[1]) return 0;
break;
@@ -8049,6 +8254,10 @@ the end of the branch, it is called to skip over an internal lookaround, and it
is also called to skip to the end of a class, during which it will never
encounter nested groups (but there's no need to have special code for that).
+When called to find the end of a branch or group, pptr must point to the first
+meta code inside the branch, not the branch-starting code. In other cases it
+can point to the item that causes the function to be called.
+
Arguments:
pptr current pointer to skip from
skiptype PSKIP_CLASS when skipping to end of class
@@ -8065,7 +8274,7 @@ parsed_skip(uint32_t *pptr, uint32_t skiptype)
{
uint32_t nestlevel = 0;
-for (pptr += 1;; pptr++)
+for (;; pptr++)
{
uint32_t meta = META_CODE(*pptr);
@@ -8160,11 +8369,12 @@ return pptr;
/* This is called for nested groups within a branch of a lookbehind whose
length is being computed. If all the branches in the nested group have the same
length, that is OK. On entry, the pointer must be at the first element after
-the group initializing code. Caching is used to improve processing speed when
-the same capturing group occurs many times.
+the group initializing code. On exit it points to OP_KET. Caching is used to
+improve processing speed when the same capturing group occurs many times.
Arguments:
pptrptr pointer to pointer in the parsed pattern
+ isinline FALSE if a reference or recursion; TRUE for inline group
errcodeptr pointer to the errorcode
lcptr pointer to the loop counter
group number of captured group or -1 for a non-capturing group
@@ -8175,27 +8385,29 @@ Returns: the group length or a negative number
*/
static int
-get_grouplength(uint32_t **pptrptr, int *errcodeptr, int *lcptr,
+get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr,
int group, parsed_recurse_check *recurses, compile_block *cb)
{
int branchlength;
int grouplength = -1;
/* The cache can be used only if there is no possibility of there being two
-groups with the same number. */
+groups with the same number. We do not need to set the end pointer for a group
+that is being processed as a back reference or recursion, but we must do so for
+an inline group. */
-if (group > 0)
+if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)
{
uint32_t groupinfo = cb->groupinfo[group];
- if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0)
+ if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1;
+ if ((groupinfo & GI_SET_FIXED_LENGTH) != 0)
{
- if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1;
- if ((groupinfo & GI_SET_FIXED_LENGTH) != 0)
- return groupinfo & GI_FIXED_LENGTH_MASK;
+ if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET);
+ return groupinfo & GI_FIXED_LENGTH_MASK;
}
}
-/* Scan the group */
+/* Scan the group. In this case we find the end pointer of necessity. */
for(;;)
{
@@ -8353,11 +8565,12 @@ for (;; pptr++)
}
break;
- /* Lookaheads can be ignored. */
+ /* Lookaheads can be ignored, but we must start the skip inside the group
+ so that it isn't treated as a group within the branch. */
case META_LOOKAHEAD:
case META_LOOKAHEADNOT:
- pptr = parsed_skip(pptr, PSKIP_KET);
+ pptr = parsed_skip(pptr + 1, PSKIP_KET);
if (pptr == NULL) goto PARSED_SKIP_FAILED;
break;
@@ -8378,6 +8591,7 @@ for (;; pptr++)
case META_BACKREF_BYNAME:
if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0)
goto ISNOTFIXED;
+ /* Fall through */
case META_RECURSE_BYNAME:
{
@@ -8432,7 +8646,8 @@ for (;; pptr++)
goto RECURSE_OR_BACKREF_LENGTH;
}
- /* Fall through for groups >= 10 - picking up group twice does no harm. */
+ /* Fall through */
+ /* For groups >= 10 - picking up group twice does no harm. */
/* A true recursion implies not fixed length, but a subroutine call may
be OK. Back reference "recursions" are also failed. */
@@ -8455,15 +8670,24 @@ for (;; pptr++)
else if (*gptr == (META_CAPTURE | group)) break;
}
- gptrend = parsed_skip(gptr, PSKIP_KET);
+ /* We must start the search for the end of the group at the first meta code
+ inside the group. Otherwise it will be treated as an enclosed group. */
+
+ gptrend = parsed_skip(gptr + 1, PSKIP_KET);
if (gptrend == NULL) goto PARSED_SKIP_FAILED;
if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */
for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break;
if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */
this_recurse.prev = recurses;
this_recurse.groupptr = gptr;
+
+ /* We do not need to know the position of the end of the group, that is,
+ gptr is not used after the call to get_grouplength(). Setting the second
+ argument FALSE stops it scanning for the end when the length can be found
+ in the cache. */
+
gptr++;
- grouplength = get_grouplength(&gptr, errcodeptr, lcptr, group,
+ grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group,
&this_recurse, cb);
if (grouplength < 0)
{
@@ -8500,7 +8724,8 @@ for (;; pptr++)
case META_NOCAPTURE:
pptr++;
CHECK_GROUP:
- grouplength = get_grouplength(&pptr, errcodeptr, lcptr, group, recurses, cb);
+ grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group,
+ recurses, cb);
if (grouplength < 0) return -1;
itemlength = grouplength;
break;
@@ -8760,7 +8985,7 @@ pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options,
int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext)
{
BOOL utf; /* Set TRUE for UTF mode */
-BOOL has_lookbehind; /* Set TRUE if a lookbehind is found */
+BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */
BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */
pcre2_real_code *re = NULL; /* What we will return */
compile_block cb; /* "Static" compile-time data */
@@ -8782,8 +9007,9 @@ uint32_t firstcu, reqcu; /* Value of first/req code unit */
uint32_t setflags = 0; /* NL and BSR set flags */
uint32_t skipatstart; /* When checking (*UTF) etc */
+uint32_t limit_heap = UINT32_MAX;
uint32_t limit_match = UINT32_MAX; /* Unset match limits */
-uint32_t limit_recursion = UINT32_MAX;
+uint32_t limit_depth = UINT32_MAX;
int newline = 0; /* Unset; can be set by the pattern */
int bsr = 0; /* Unset; can be set by the pattern */
@@ -8821,18 +9047,27 @@ if (pattern == NULL)
return NULL;
}
+/* A NULL compile context means "use a default context" */
+
+if (ccontext == NULL)
+ ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context));
+
/* Check that all undefined public option bits are zero. */
-if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0)
+if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0)
{
*errorptr = ERR17;
return NULL;
}
-/* A NULL compile context means "use a default context" */
-
-if (ccontext == NULL)
- ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context));
+if ((options & PCRE2_LITERAL) != 0 &&
+ ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0))
+ {
+ *errorptr = ERR92;
+ return NULL;
+ }
/* A zero-terminated pattern is indicated by the special length value
PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */
@@ -8907,10 +9142,11 @@ for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET;
/* --------------- Start looking at the pattern --------------- */
-/* Check for global one-time option settings at the start of the pattern, and
-remember the offset to the actual regex. With valgrind support, make the
-terminator of a zero-terminated pattern inaccessible. This catches bugs that
-would otherwise only show up for non-zero-terminated patterns. */
+/* Unless PCRE2_LITERAL is set, check for global one-time option settings at
+the start of the pattern, and remember the offset to the actual regex. With
+valgrind support, make the terminator of a zero-terminated pattern
+inaccessible. This catches bugs that would otherwise only show up for
+non-zero-terminated patterns. */
#ifdef SUPPORT_VALGRIND
if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1));
@@ -8919,70 +9155,75 @@ if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1));
ptr = pattern;
skipatstart = 0;
-while (patlen - skipatstart >= 2 &&
- ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&
- ptr[skipatstart+1] == CHAR_ASTERISK)
+if ((options & PCRE2_LITERAL) == 0)
{
- for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++)
+ while (patlen - skipatstart >= 2 &&
+ ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&
+ ptr[skipatstart+1] == CHAR_ASTERISK)
{
- pso *p = pso_list + i;
-
- if (patlen - skipatstart - 2 >= p->length &&
- PRIV(strncmp_c8)(ptr+skipatstart+2, (char *)(p->name), p->length) == 0)
+ for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++)
{
uint32_t c, pp;
+ pso *p = pso_list + i;
- skipatstart += p->length + 2;
- switch(p->type)
+ if (patlen - skipatstart - 2 >= p->length &&
+ PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name),
+ p->length) == 0)
{
- case PSO_OPT:
- cb.external_options |= p->value;
- break;
+ skipatstart += p->length + 2;
+ switch(p->type)
+ {
+ case PSO_OPT:
+ cb.external_options |= p->value;
+ break;
- case PSO_FLG:
- setflags |= p->value;
- break;
+ case PSO_FLG:
+ setflags |= p->value;
+ break;
- case PSO_NL:
- newline = p->value;
- setflags |= PCRE2_NL_SET;
- break;
+ case PSO_NL:
+ newline = p->value;
+ setflags |= PCRE2_NL_SET;
+ break;
- case PSO_BSR:
- bsr = p->value;
- setflags |= PCRE2_BSR_SET;
- break;
+ case PSO_BSR:
+ bsr = p->value;
+ setflags |= PCRE2_BSR_SET;
+ break;
- case PSO_LIMM:
- case PSO_LIMR:
- c = 0;
- pp = skipatstart;
- if (!IS_DIGIT(ptr[pp]))
- {
- errorcode = ERR60;
- ptr += pp;
- goto HAD_EARLY_ERROR;
- }
- while (IS_DIGIT(ptr[pp]))
- {
- if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */
- c = c*10 + (ptr[pp++] - CHAR_0);
- }
- if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS)
- {
- errorcode = ERR60;
- ptr += pp;
- goto HAD_EARLY_ERROR;
+ case PSO_LIMM:
+ case PSO_LIMD:
+ case PSO_LIMH:
+ c = 0;
+ pp = skipatstart;
+ if (!IS_DIGIT(ptr[pp]))
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ while (IS_DIGIT(ptr[pp]))
+ {
+ if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */
+ c = c*10 + (ptr[pp++] - CHAR_0);
+ }
+ if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ if (p->type == PSO_LIMH) limit_heap = c;
+ else if (p->type == PSO_LIMM) limit_match = c;
+ else limit_depth = c;
+ skipatstart += pp - skipatstart;
+ break;
}
- if (p->type == PSO_LIMM) limit_match = c;
- else limit_recursion = c;
- skipatstart += pp - skipatstart;
- break;
+ break; /* Out of the table scan loop */
}
- break; /* Out of the table scan loop */
}
+ if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */
}
- if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */
}
/* End of pattern-start options; advance to start of real regex. */
@@ -9000,7 +9241,9 @@ if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0)
#endif
/* Check UTF. We have the original options in 'options', with that value as
-modified by (*UTF) etc in cb->external_options. */
+modified by (*UTF) etc in cb->external_options. The extra option
+PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the
+surrogate code points cannot be represented in UTF-16. */
utf = (cb.external_options & PCRE2_UTF) != 0;
if (utf)
@@ -9013,6 +9256,14 @@ if (utf)
if ((options & PCRE2_NO_UTF_CHECK) == 0 &&
(errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0)
goto HAD_ERROR; /* Offset was set by valid_utf() */
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)
+ {
+ errorcode = ERR91;
+ goto HAD_EARLY_ERROR;
+ }
+#endif
}
/* Check UCP lockout. */
@@ -9044,6 +9295,11 @@ switch(newline)
cb.nl[0] = CHAR_NL;
break;
+ case PCRE2_NEWLINE_NUL:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_NUL;
+ break;
+
case PCRE2_NEWLINE_CRLF:
cb.nllen = 2;
cb.nl[0] = CHAR_CR;
@@ -9071,10 +9327,10 @@ and comments removed (amongst other things).
In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned
32-bit ints in the parsed pattern is bounded by the length of the pattern plus
-one (for the terminator). The exceptional case is when running in 32-bit,
-non-UTF mode, when literal characters greater than META_END (0x80000000) have
-to be coded as two units. In this case, therefore, we scan the pattern to check
-for such values. */
+one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is
+set. The exceptional case is when running in 32-bit, non-UTF mode, when literal
+characters greater than META_END (0x80000000) have to be coded as two units. In
+this case, therefore, we scan the pattern to check for such values. */
#if PCRE2_CODE_UNIT_WIDTH == 32
if (!utf)
@@ -9091,6 +9347,11 @@ many smaller patterns the vector on the stack (which was set up above) can be
used. */
parsed_size_needed = patlen - skipatstart + big32count;
+
+if ((ccontext->extra_options &
+ (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0)
+ parsed_size_needed += 4;
+
if ((options & PCRE2_AUTO_CALLOUT) != 0)
parsed_size_needed = (parsed_size_needed + 1) * 5;
@@ -9199,7 +9460,8 @@ possible because nowadays we limit the maximum value of cb.names_found and
cb.name_entry_size. */
re_blocksize = sizeof(pcre2_real_code) +
- CU2BYTES(length + cb.names_found * cb.name_entry_size);
+ CU2BYTES(length +
+ (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size);
re = (pcre2_real_code *)
ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data);
if (re == NULL)
@@ -9208,6 +9470,13 @@ if (re == NULL)
goto HAD_CB_ERROR;
}
+/* The compiler may put padding at the end of the pcre2_real_code structure in
+order to round it up to a multiple of 4 or 8 bytes. This means that when a
+compiled pattern is copied (for example, when serialized) undefined bytes are
+read, and this annoys debuggers such as valgrind. To avoid this, we explicitly
+write to the last 8 bytes of the structure before setting the fields. */
+
+memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8);
re->memctl = ccontext->memctl;
re->tables = tables;
re->executable_jit = NULL;
@@ -9216,9 +9485,11 @@ re->blocksize = re_blocksize;
re->magic_number = MAGIC_NUMBER;
re->compile_options = options;
re->overall_options = cb.external_options;
+re->extra_options = ccontext->extra_options;
re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags;
+re->limit_heap = limit_heap;
re->limit_match = limit_match;
-re->limit_recursion = limit_recursion;
+re->limit_depth = limit_depth;
re->first_codeunit = 0;
re->last_codeunit = 0;
re->bsr_convention = bsr;
@@ -9390,16 +9661,21 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
is_anchored(codestart, 0, &cb, 0, FALSE))
re->overall_options |= PCRE2_ANCHORED;
-/* If the pattern is still not anchored and we do not have a first code unit,
-see if there is one that is asserted (these are not saved during the compile
-because they can cause conflicts with actual literals that follow). This code
-need not be obeyed if PCRE2_NO_START_OPTIMIZE is set, as the data it would
-create will not be used. */
+/* Set up the first code unit or startline flag, the required code unit, and
+then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE
+is set, as the data it would create will not be used. Note that a first code
+unit (but not the startline flag) is useful for anchored patterns because it
+can still give a quick "no match" and also avoid searching for a last code
+unit. */
-if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0)
+if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
{
+ /* If we do not have a first code unit, see if there is one that is asserted
+ (these are not saved during the compile because they can cause conflicts with
+ actual literals that follow). */
+
if (firstcuflags < 0)
- firstcu = find_firstassertedcu(codestart, &firstcuflags, FALSE);
+ firstcu = find_firstassertedcu(codestart, &firstcuflags, 0);
/* Save the data for a first code unit. */
@@ -9430,52 +9706,50 @@ if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0)
}
}
- /* When there is no first code unit, see if we can set the PCRE2_STARTLINE
- flag. This is helpful for multiline matches when all branches start with ^
- and also when all branches start with non-atomic .* for non-DOTALL matches
- when *PRUNE and SKIP are not present. (There is an option that disables this
- case.) */
+ /* When there is no first code unit, for non-anchored patterns, see if we can
+ set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all
+ branches start with ^ and also when all branches start with non-atomic .* for
+ non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option
+ that disables this case.) */
- else if (is_startline(codestart, 0, &cb, 0, FALSE))
+ else if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
+ is_startline(codestart, 0, &cb, 0, FALSE))
re->flags |= PCRE2_STARTLINE;
- }
-/* Handle the "required code unit", if one is set. In the case of an anchored
-pattern, do this only if it follows a variable length item in the pattern.
-Again, skip this if PCRE2_NO_START_OPTIMIZE is set. */
+ /* Handle the "required code unit", if one is set. In the case of an anchored
+ pattern, do this only if it follows a variable length item in the pattern. */
-if (reqcuflags >= 0 &&
- ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0 ||
- (reqcuflags & REQ_VARY) != 0))
- {
- re->last_codeunit = reqcu;
- re->flags |= PCRE2_LASTSET;
+ if (reqcuflags >= 0 &&
+ ((re->overall_options & PCRE2_ANCHORED) == 0 ||
+ (reqcuflags & REQ_VARY) != 0))
+ {
+ re->last_codeunit = reqcu;
+ re->flags |= PCRE2_LASTSET;
- /* Handle caseless required code units as for first code units (above). */
+ /* Handle caseless required code units as for first code units (above). */
- if ((reqcuflags & REQ_CASELESS) != 0)
- {
- if (reqcu < 128 || (!utf && reqcu < 255))
+ if ((reqcuflags & REQ_CASELESS) != 0)
{
- if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS;
- }
+ if (reqcu < 128 || (!utf && reqcu < 255))
+ {
+ if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS;
+ }
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8
- else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu)
- re->flags |= PCRE2_LASTCASELESS;
+ else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu)
+ re->flags |= PCRE2_LASTCASELESS;
#endif
+ }
}
- }
-/* Finally, unless PCRE2_NO_START_OPTIMIZE is set, study the compiled pattern
-to set up information such as a bitmap of starting code units and a minimum
-matching length. */
+ /* Finally, study the compiled pattern to set up information such as a bitmap
+ of starting code units and a minimum matching length. */
-if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 &&
- PRIV(study)(re) != 0)
- {
- errorcode = ERR31;
- goto HAD_CB_ERROR;
- }
+ if (PRIV(study)(re) != 0)
+ {
+ errorcode = ERR31;
+ goto HAD_CB_ERROR;
+ }
+ } /* End of start-of-match optimizations. */
/* Control ends up here in all cases. When running under valgrind, make a
pattern's terminating zero defined again. If memory was obtained for the parsed
diff --git a/thirdparty/pcre2/src/pcre2_config.c b/thirdparty/pcre2/src/pcre2_config.c
index e99272f577..e487b10220 100644
--- a/thirdparty/pcre2/src/pcre2_config.c
+++ b/thirdparty/pcre2/src/pcre2_config.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -84,13 +84,16 @@ if (where == NULL) /* Requests a length */
return PCRE2_ERROR_BADOPTION;
case PCRE2_CONFIG_BSR:
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ case PCRE2_CONFIG_HEAPLIMIT:
case PCRE2_CONFIG_JIT:
case PCRE2_CONFIG_LINKSIZE:
case PCRE2_CONFIG_MATCHLIMIT:
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
case PCRE2_CONFIG_NEWLINE:
case PCRE2_CONFIG_PARENSLIMIT:
- case PCRE2_CONFIG_RECURSIONLIMIT:
- case PCRE2_CONFIG_STACKRECURSE:
+ case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */
case PCRE2_CONFIG_UNICODE:
return sizeof(uint32_t);
@@ -116,6 +119,28 @@ switch (what)
#endif
break;
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ *((uint32_t *)where) = 0
+#ifdef SUPPORT_PCRE2_8
+ + 1
+#endif
+#ifdef SUPPORT_PCRE2_16
+ + 2
+#endif
+#ifdef SUPPORT_PCRE2_32
+ + 4
+#endif
+ ;
+ break;
+
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ *((uint32_t *)where) = MATCH_LIMIT_DEPTH;
+ break;
+
+ case PCRE2_CONFIG_HEAPLIMIT:
+ *((uint32_t *)where) = HEAP_LIMIT;
+ break;
+
case PCRE2_CONFIG_JIT:
#ifdef SUPPORT_JIT
*((uint32_t *)where) = 1;
@@ -147,20 +172,23 @@ switch (what)
*((uint32_t *)where) = NEWLINE_DEFAULT;
break;
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
+#ifdef NEVER_BACKSLASH_C
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
case PCRE2_CONFIG_PARENSLIMIT:
*((uint32_t *)where) = PARENS_NEST_LIMIT;
break;
- case PCRE2_CONFIG_RECURSIONLIMIT:
- *((uint32_t *)where) = MATCH_LIMIT_RECURSION;
- break;
+ /* This is now obsolete. The stack is no longer used via recursion for
+ handling backtracking in pcre2_match(). */
case PCRE2_CONFIG_STACKRECURSE:
-#ifdef HEAP_MATCH_RECURSE
*((uint32_t *)where) = 0;
-#else
- *((uint32_t *)where) = 1;
-#endif
break;
case PCRE2_CONFIG_UNICODE_VERSION:
diff --git a/thirdparty/pcre2/src/pcre2_context.c b/thirdparty/pcre2/src/pcre2_context.c
index ae050fe92c..2c14df0080 100644
--- a/thirdparty/pcre2/src/pcre2_context.c
+++ b/thirdparty/pcre2/src/pcre2_context.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -138,7 +138,8 @@ const pcre2_compile_context PRIV(default_compile_context) = {
PCRE2_UNSET, /* Max pattern length */
BSR_DEFAULT, /* Backslash R default */
NEWLINE_DEFAULT, /* Newline convention */
- PARENS_NEST_LIMIT }; /* As it says */
+ PARENS_NEST_LIMIT, /* As it says */
+ 0 }; /* Extra options */
/* The create function copies the default into the new memory, but must
override the default memory handling functions if a gcontext was provided. */
@@ -161,9 +162,6 @@ when no context is supplied to a match function. */
const pcre2_match_context PRIV(default_match_context) = {
{ default_malloc, default_free, NULL },
-#ifdef HEAP_MATCH_RECURSE
- { default_malloc, default_free, NULL },
-#endif
#ifdef SUPPORT_JIT
NULL,
NULL,
@@ -171,8 +169,9 @@ const pcre2_match_context PRIV(default_match_context) = {
NULL,
NULL,
PCRE2_UNSET, /* Offset limit */
+ HEAP_LIMIT,
MATCH_LIMIT,
- MATCH_LIMIT_RECURSION };
+ MATCH_LIMIT_DEPTH };
/* The create function copies the default into the new memory, but must
override the default memory handling functions if a gcontext was provided. */
@@ -190,6 +189,36 @@ return mcontext;
}
+/* A default convert context is set up to save having to initialize at run time
+when no context is supplied to the convert function. */
+
+const pcre2_convert_context PRIV(default_convert_context) = {
+ { default_malloc, default_free, NULL }, /* Default memory handling */
+#ifdef _WIN32
+ CHAR_BACKSLASH, /* Default path separator */
+ CHAR_GRAVE_ACCENT /* Default escape character */
+#else /* Not Windows */
+ CHAR_SLASH, /* Default path separator */
+ CHAR_BACKSLASH /* Default escape character */
+#endif
+ };
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_create(pcre2_general_context *gcontext)
+{
+pcre2_convert_context *ccontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext);
+if (ccontext == NULL) return NULL;
+*ccontext = PRIV(default_convert_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);
+return ccontext;
+}
+
+
/*************************************************
* Context copy functions *
*************************************************/
@@ -231,11 +260,22 @@ return new;
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_copy(pcre2_convert_context *ccontext)
+{
+pcre2_convert_context *new =
+ ccontext->memctl.malloc(sizeof(pcre2_real_convert_context),
+ ccontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, ccontext, sizeof(pcre2_real_convert_context));
+return new;
+}
+
+
/*************************************************
* Context free functions *
*************************************************/
-
PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
pcre2_general_context_free(pcre2_general_context *gcontext)
{
@@ -260,6 +300,12 @@ if (mcontext != NULL)
}
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_convert_context_free(pcre2_convert_context *ccontext)
+{
+if (ccontext != NULL)
+ ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);
+}
/*************************************************
@@ -271,7 +317,7 @@ data is given. Only some of the functions are able to test the validity of the
data. */
-/* ------------ Compile contexts ------------ */
+/* ------------ Compile context ------------ */
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_character_tables(pcre2_compile_context *ccontext,
@@ -313,6 +359,7 @@ switch(newline)
case PCRE2_NEWLINE_CRLF:
case PCRE2_NEWLINE_ANY:
case PCRE2_NEWLINE_ANYCRLF:
+ case PCRE2_NEWLINE_NUL:
ccontext->newline_convention = newline;
return 0;
@@ -329,6 +376,13 @@ return 0;
}
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options)
+{
+ccontext->extra_options = options;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext,
int (*guard)(uint32_t, void *), void *user_data)
{
@@ -338,7 +392,7 @@ return 0;
}
-/* ------------ Match contexts ------------ */
+/* ------------ Match context ------------ */
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_callout(pcre2_match_context *mcontext,
@@ -350,6 +404,13 @@ return 0;
}
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->heap_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit)
{
mcontext->match_limit = limit;
@@ -357,17 +418,26 @@ return 0;
}
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->depth_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit)
{
mcontext->offset_limit = limit;
return 0;
}
+/* This function became obsolete at release 10.30. It is kept as a synonym for
+backwards compatibility. */
+
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit)
{
-mcontext->recursion_limit = limit;
-return 0;
+return pcre2_set_depth_limit(mcontext, limit);
}
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
@@ -375,17 +445,32 @@ pcre2_set_recursion_memory_management(pcre2_match_context *mcontext,
void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *),
void *mydata)
{
-#ifdef HEAP_MATCH_RECURSE
-mcontext->stack_memctl.malloc = mymalloc;
-mcontext->stack_memctl.free = myfree;
-mcontext->stack_memctl.memory_data = mydata;
-#else
(void)mcontext;
(void)mymalloc;
(void)myfree;
(void)mydata;
-#endif
+return 0;
+}
+
+/* ------------ Convert context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator)
+{
+if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH &&
+ separator != CHAR_DOT) return PCRE2_ERROR_BADDATA;
+ccontext->glob_separator = separator;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape)
+{
+if (escape > 255 || (escape != 0 && !ispunct(escape)))
+ return PCRE2_ERROR_BADDATA;
+ccontext->glob_escape = escape;
return 0;
}
/* End of pcre2_context.c */
+
diff --git a/thirdparty/pcre2/src/pcre2_convert.c b/thirdparty/pcre2/src/pcre2_convert.c
new file mode 100644
index 0000000000..bdf9b86df6
--- /dev/null
+++ b/thirdparty/pcre2/src/pcre2_convert.c
@@ -0,0 +1,1176 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pcre2_internal.h"
+
+#define TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \
+ PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED)
+
+#define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \
+ PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \
+ PCRE2_CONVERT_GLOB_NO_STARSTAR| \
+ TYPE_OPTIONS)
+
+#define DUMMY_BUFFER_SIZE 100
+
+/* Generated pattern fragments */
+
+#define STR_BACKSLASH_A STR_BACKSLASH STR_A
+#define STR_BACKSLASH_z STR_BACKSLASH STR_z
+#define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET
+#define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN
+#define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS
+#define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS
+#define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
+
+/* States for range and POSIX processing */
+
+enum { RANGE_NOT_STARTED, RANGE_STARTING, RANGE_STARTED };
+enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET,
+ POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED };
+
+/* Macro to add a character string to the output buffer, checking for overflow. */
+
+#define PUTCHARS(string) \
+ { \
+ for (s = (char *)(string); *s != 0; s++) \
+ { \
+ if (p >= endp) return PCRE2_ERROR_NOMEMORY; \
+ *p++ = *s; \
+ } \
+ }
+
+/* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */
+
+static const char *pcre2_escaped_literals =
+ STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS
+ STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS;
+
+/* Recognized escaped metacharacters in POSIX basic patterns. */
+
+static const char *posix_meta_escapes =
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9;
+
+
+
+/*************************************************
+* Convert a POSIX pattern *
+*************************************************/
+
+/* This function handles both basic and extended POSIX patterns.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+char *s;
+PCRE2_SPTR posix = pattern;
+PCRE2_UCHAR *p = use_buffer;
+PCRE2_UCHAR *pp = p;
+PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */
+PCRE2_SIZE convlength = 0;
+
+uint32_t bracount = 0;
+uint32_t posix_state = POSIX_START_REGEX;
+uint32_t lastspecial = 0;
+BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0;
+BOOL nextisliteral = FALSE;
+
+(void)utf; /* Not used when Unicode not supported */
+(void)ccontext; /* Not currently used */
+
+/* Initialize default for error offset as end of input. */
+
+*bufflenptr = plength;
+PUTCHARS(STR_STAR_NUL);
+
+/* Now scan the input. */
+
+while (plength > 0)
+ {
+ uint32_t c, sc;
+ int clength = 1;
+
+ /* Add in the length of the last item, then, if in the dummy run, pull the
+ pointer back to the start of the (temporary) buffer and then remember the
+ start of the next item. */
+
+ convlength += p - pp;
+ if (dummyrun) p = use_buffer;
+ pp = p;
+
+ /* Pick up the next character */
+
+#ifndef SUPPORT_UNICODE
+ c = *posix;
+#else
+ GETCHARLENTEST(c, posix, clength);
+#endif
+ posix += clength;
+ plength -= clength;
+
+ sc = nextisliteral? 0 : c;
+ nextisliteral = FALSE;
+
+ /* Handle a character within a class. */
+
+ if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ {
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ posix_state = POSIX_NOT_BRACKET;
+ }
+
+ /* Not the end of the class */
+
+ else
+ {
+ switch (posix_state)
+ {
+ case POSIX_CLASS_STARTED:
+ if (c <= 127 && islower(c)) break; /* Remain in started state */
+ posix_state = POSIX_CLASS_NOT_STARTED;
+ if (c == CHAR_COLON && plength > 0 &&
+ *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET);
+ plength--;
+ posix++;
+ continue; /* With next character after :] */
+ }
+ /* Fall through */
+
+ case POSIX_CLASS_NOT_STARTED:
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ posix_state = POSIX_CLASS_STARTING;
+ break;
+
+ case POSIX_CLASS_STARTING:
+ if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED;
+ break;
+ }
+
+ if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH);
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ }
+ }
+
+ /* Handle a character not within a class. */
+
+ else switch(sc)
+ {
+ case CHAR_LEFT_SQUARE_BRACKET:
+ PUTCHARS(STR_LEFT_SQUARE_BRACKET);
+
+#ifdef NEVER
+ /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does
+ support) but they are not part of POSIX 1003.1. */
+
+ if (plength >= 6)
+ {
+ if (posix[0] == CHAR_LEFT_SQUARE_BRACKET &&
+ posix[1] == CHAR_COLON &&
+ (posix[2] == CHAR_LESS_THAN_SIGN ||
+ posix[2] == CHAR_GREATER_THAN_SIGN) &&
+ posix[3] == CHAR_COLON &&
+ posix[4] == CHAR_RIGHT_SQUARE_BRACKET &&
+ posix[5] == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix, CU2BYTES(6));
+ p += 6;
+ posix += 6;
+ plength -= 6;
+ continue; /* With next character */
+ }
+ }
+#endif
+
+ /* Handle start of "normal" character classes */
+
+ posix_state = POSIX_CLASS_NOT_STARTED;
+
+ /* Handle ^ and ] as first characters */
+
+ if (plength > 0)
+ {
+ if (*posix == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_CIRCUMFLEX_ACCENT);
+ }
+ if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ }
+ }
+ break;
+
+ case CHAR_BACKSLASH:
+ if (plength <= 0) return PCRE2_ERROR_END_BACKSLASH;
+ if (extended) nextisliteral = TRUE; else
+ {
+ if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL)
+ {
+ if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH);
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ lastspecial = *p++ = *posix++;
+ plength--;
+ }
+ else nextisliteral = TRUE;
+ }
+ break;
+
+ case CHAR_RIGHT_PARENTHESIS:
+ if (!extended || bracount == 0) goto ESCAPE_LITERAL;
+ bracount--;
+ goto COPY_SPECIAL;
+
+ case CHAR_LEFT_PARENTHESIS:
+ bracount++;
+ /* Fall through */
+
+ case CHAR_QUESTION_MARK:
+ case CHAR_PLUS:
+ case CHAR_LEFT_CURLY_BRACKET:
+ case CHAR_RIGHT_CURLY_BRACKET:
+ case CHAR_VERTICAL_LINE:
+ if (!extended) goto ESCAPE_LITERAL;
+ /* Fall through */
+
+ case CHAR_DOT:
+ case CHAR_DOLLAR_SIGN:
+ posix_state = POSIX_NOT_BRACKET;
+ COPY_SPECIAL:
+ lastspecial = c;
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ *p++ = c;
+ break;
+
+ case CHAR_ASTERISK:
+ if (lastspecial != CHAR_ASTERISK)
+ {
+ if (!extended && (posix_state < POSIX_NOT_BRACKET ||
+ lastspecial == CHAR_LEFT_PARENTHESIS))
+ goto ESCAPE_LITERAL;
+ goto COPY_SPECIAL;
+ }
+ break; /* Ignore second and subsequent asterisks */
+
+ case CHAR_CIRCUMFLEX_ACCENT:
+ if (extended) goto COPY_SPECIAL;
+ if (posix_state == POSIX_START_REGEX ||
+ lastspecial == CHAR_LEFT_PARENTHESIS)
+ {
+ posix_state = POSIX_ANCHORED;
+ goto COPY_SPECIAL;
+ }
+ /* Fall through */
+
+ default:
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ {
+ ESCAPE_LITERAL:
+ PUTCHARS(STR_BACKSLASH);
+ }
+ lastspecial = 0xff; /* Indicates nothing special */
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ posix_state = POSIX_NOT_BRACKET;
+ break;
+ }
+ }
+
+if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+convlength += p - pp; /* Final segment */
+*bufflenptr = convlength;
+*p++ = 0;
+return 0;
+}
+
+
+/*************************************************
+* Convert a glob pattern *
+*************************************************/
+
+/* Context for writing the output into a buffer. */
+
+typedef struct pcre2_output_context {
+ PCRE2_UCHAR *output; /* current output position */
+ PCRE2_SPTR output_end; /* output end */
+ PCRE2_SIZE output_size; /* size of the output */
+ uint8_t out_str[8]; /* string copied to the output */
+} pcre2_output_context;
+
+
+/* Write a character into the output.
+
+Arguments:
+ out output context
+ chr the next character
+*/
+
+static void
+convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr)
+{
+out->output_size++;
+
+if (out->output < out->output_end)
+ *out->output++ = chr;
+}
+
+
+/* Write a string into the output.
+
+Arguments:
+ out output context
+ length length of out->out_str
+*/
+
+static void
+convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length)
+{
+uint8_t *out_str = out->out_str;
+PCRE2_UCHAR *output = out->output;
+PCRE2_SPTR output_end = out->output_end;
+PCRE2_SIZE output_size = out->output_size;
+
+do
+ {
+ output_size++;
+
+ if (output < output_end)
+ *output++ = *out_str++;
+ }
+while (--length != 0);
+
+out->output = output;
+out->output_size = output_size;
+}
+
+
+/* Prints the separator into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_separator(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+if (with_escape)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+convert_glob_write(out, separator);
+}
+
+
+/* Prints a wildcard into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_wildcard(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+convert_glob_write_str(out, 2);
+
+convert_glob_print_separator(out, separator, with_escape);
+
+convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET);
+}
+
+
+/* Parse a posix class.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+
+Returns: >0 => class index
+ 0 => malformed class
+*/
+
+static int
+convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out)
+{
+static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:"
+ "graph:lower:print:punct:space:upper:word:xdigit:";
+PCRE2_SPTR start = *from + 1;
+PCRE2_SPTR pattern = start;
+const char *class_ptr;
+PCRE2_UCHAR c;
+int class_index;
+
+while (TRUE)
+ {
+ if (pattern >= pattern_end) return 0;
+
+ c = *pattern++;
+
+ if (c < CHAR_a || c > CHAR_z) break;
+ }
+
+if (c != CHAR_COLON || pattern >= pattern_end ||
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ return 0;
+
+class_ptr = posix_classes;
+class_index = 1;
+
+while (TRUE)
+ {
+ if (*class_ptr == CHAR_NUL) return 0;
+
+ pattern = start;
+
+ while (*pattern == (PCRE2_UCHAR) *class_ptr)
+ {
+ if (*pattern == CHAR_COLON)
+ {
+ pattern += 2;
+ start -= 2;
+
+ do convert_glob_write(out, *start++); while (start < pattern);
+
+ *from = pattern;
+ return class_index;
+ }
+ pattern++;
+ class_ptr++;
+ }
+
+ while (*class_ptr != CHAR_COLON) class_ptr++;
+ class_ptr++;
+ class_index++;
+ }
+}
+
+/* Checks whether the character is in the class.
+
+Arguments:
+ class_index class index
+ c character
+
+Returns: !0 => character is found in the class
+ 0 => otherwise
+*/
+
+static BOOL
+convert_glob_char_in_class(int class_index, PCRE2_UCHAR c)
+{
+switch (class_index)
+ {
+ case 1: return isalnum(c);
+ case 2: return isalpha(c);
+ case 3: return 1;
+ case 4: return c == CHAR_HT || c == CHAR_SPACE;
+ case 5: return iscntrl(c);
+ case 6: return isdigit(c);
+ case 7: return isgraph(c);
+ case 8: return islower(c);
+ case 9: return isprint(c);
+ case 10: return ispunct(c);
+ case 11: return isspace(c);
+ case 12: return isupper(c);
+ case 13: return isalnum(c) || c == CHAR_UNDERSCORE;
+ default: return isxdigit(c);
+ }
+}
+
+/* Parse a range of characters.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator,
+ BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep)
+{
+BOOL is_negative = FALSE;
+BOOL separator_seen = FALSE;
+BOOL has_prev_c;
+PCRE2_SPTR pattern = *from;
+PCRE2_SPTR char_start = NULL;
+uint32_t c, prev_c;
+int len, class_index;
+
+(void)utf; /* Avoid compiler warning. */
+
+if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+if (*pattern == CHAR_EXCLAMATION_MARK
+ || *pattern == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ pattern++;
+
+ if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+ is_negative = TRUE;
+
+ out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+ out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+ len = 2;
+
+ if (!no_wildsep)
+ {
+ if (with_escape)
+ {
+ out->out_str[len] = CHAR_BACKSLASH;
+ len++;
+ }
+ out->out_str[len] = (uint8_t) separator;
+ }
+
+ convert_glob_write_str(out, len + 1);
+ }
+else
+ convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET);
+
+has_prev_c = FALSE;
+prev_c = 0;
+
+if (*pattern == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ out->out_str[0] = CHAR_BACKSLASH;
+ out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET;
+ convert_glob_write_str(out, 2);
+ has_prev_c = TRUE;
+ prev_c = CHAR_RIGHT_SQUARE_BRACKET;
+ pattern++;
+ }
+
+while (pattern < pattern_end)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, c);
+
+ if (!is_negative && !no_wildsep && separator_seen)
+ {
+ out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out->out_str[1] = CHAR_QUESTION_MARK;
+ out->out_str[2] = CHAR_LESS_THAN_SIGN;
+ out->out_str[3] = CHAR_EXCLAMATION_MARK;
+ convert_glob_write_str(out, 4);
+
+ convert_glob_print_separator(out, separator, with_escape);
+ convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+ }
+
+ *from = pattern;
+ return 0;
+ }
+
+ if (pattern >= pattern_end) break;
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ class_index = convert_glob_parse_class(from, pattern_end, out);
+
+ if (class_index != 0)
+ {
+ pattern = *from;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+
+ if (!is_negative &&
+ convert_glob_char_in_class (class_index, separator))
+ separator_seen = TRUE;
+ continue;
+ }
+ }
+ else if (c == CHAR_MINUS && has_prev_c &&
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, CHAR_MINUS);
+
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+ }
+ else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c > c)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c < separator && separator < c) separator_seen = TRUE;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+ }
+ else
+ {
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+ }
+
+ has_prev_c = TRUE;
+ prev_c = c;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET ||
+ c == CHAR_BACKSLASH || c == CHAR_MINUS)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+ if (c == separator) separator_seen = TRUE;
+
+ do convert_glob_write(out, *char_start++); while (char_start < pattern);
+ }
+
+*from = pattern;
+return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+}
+
+
+/* Prints a (*COMMIT) into the output.
+
+Arguments:
+ out output context
+*/
+
+static void
+convert_glob_print_commit(pcre2_output_context *out)
+{
+out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+out->out_str[1] = CHAR_ASTERISK;
+out->out_str[2] = CHAR_C;
+out->out_str[3] = CHAR_O;
+out->out_str[4] = CHAR_M;
+out->out_str[5] = CHAR_M;
+out->out_str[6] = CHAR_I;
+out->out_str[7] = CHAR_T;
+convert_glob_write_str(out, 8);
+convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+}
+
+
+/* Bash glob converter.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+pcre2_output_context out;
+PCRE2_SPTR pattern_start = pattern;
+PCRE2_SPTR pattern_end = pattern + plength;
+PCRE2_UCHAR separator = ccontext->glob_separator;
+PCRE2_UCHAR escape = ccontext->glob_escape;
+PCRE2_UCHAR c;
+BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0;
+BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0;
+BOOL in_atomic = FALSE;
+BOOL after_starstar = FALSE;
+BOOL no_slash_z = FALSE;
+BOOL with_escape, is_start, after_separator;
+int result = 0;
+
+(void)utf; /* Avoid compiler warning. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (separator >= 128 || escape >= 128))
+ {
+ /* Currently only ASCII characters are supported. */
+ *bufflenptr = 0;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+#endif
+
+with_escape = strchr(pcre2_escaped_literals, separator) != NULL;
+
+/* Initialize default for error offset as end of input. */
+out.output = use_buffer;
+out.output_end = use_buffer + use_length;
+out.output_size = 0;
+
+out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+out.out_str[1] = CHAR_QUESTION_MARK;
+out.out_str[2] = CHAR_s;
+out.out_str[3] = CHAR_RIGHT_PARENTHESIS;
+convert_glob_write_str(&out, 4);
+
+is_start = TRUE;
+
+if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK)
+ {
+ if (no_wildsep)
+ is_start = FALSE;
+ else if (!no_starstar && pattern + 1 < pattern_end &&
+ pattern[1] == CHAR_ASTERISK)
+ is_start = FALSE;
+ }
+
+if (is_start)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_A;
+ convert_glob_write_str(&out, 2);
+ }
+
+while (pattern < pattern_end)
+ {
+ c = *pattern++;
+
+ if (c == CHAR_ASTERISK)
+ {
+ is_start = pattern == pattern_start + 1;
+
+ if (in_atomic)
+ {
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+ in_atomic = FALSE;
+ }
+
+ if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ after_separator = is_start || (pattern[-2] == separator);
+
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ after_starstar = TRUE;
+
+ if (after_separator && escape != 0 && *pattern == escape &&
+ pattern + 1 < pattern_end && pattern[1] == separator)
+ pattern++;
+
+ if (is_start)
+ {
+ if (*pattern != separator) continue;
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_BACKSLASH;
+ out.out_str[4] = CHAR_A;
+ out.out_str[5] = CHAR_VERTICAL_LINE;
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ pattern++;
+ continue;
+ }
+
+ convert_glob_print_commit(&out);
+
+ if (!after_separator || *pattern != separator)
+ {
+ out.out_str[0] = CHAR_DOT;
+ out.out_str[1] = CHAR_ASTERISK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+ continue;
+ }
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_DOT;
+ out.out_str[4] = CHAR_ASTERISK;
+ out.out_str[5] = CHAR_QUESTION_MARK;
+
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_RIGHT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+
+ pattern++;
+ continue;
+ }
+
+ if (pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+ }
+
+ if (no_wildsep)
+ {
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ /* Start check must be after the end check. */
+ if (is_start) continue;
+ }
+
+ if (!is_start)
+ {
+ if (after_starstar)
+ {
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_GREATER_THAN_SIGN;
+ convert_glob_write_str(&out, 3);
+ in_atomic = TRUE;
+ }
+ else
+ convert_glob_print_commit(&out);
+ }
+
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_ASTERISK;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ if (pattern >= pattern_end)
+ out.out_str[1] = CHAR_PLUS;
+ convert_glob_write_str(&out, 2);
+ continue;
+ }
+
+ if (c == CHAR_QUESTION_MARK)
+ {
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+ continue;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ {
+ result = convert_glob_parse_range(&pattern, pattern_end,
+ &out, utf, separator, with_escape, escape, no_wildsep);
+ if (result != 0) break;
+ continue;
+ }
+
+ if (escape != 0 && c == escape)
+ {
+ if (pattern >= pattern_end)
+ {
+ result = PCRE2_ERROR_CONVERT_SYNTAX;
+ break;
+ }
+ c = *pattern++;
+ }
+
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ convert_glob_write(&out, CHAR_BACKSLASH);
+
+ convert_glob_write(&out, c);
+ }
+
+if (result == 0)
+ {
+ if (!no_slash_z)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_z;
+ convert_glob_write_str(&out, 2);
+ }
+
+ if (in_atomic)
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ convert_glob_write(&out, CHAR_NUL);
+
+ if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer))
+ result = PCRE2_ERROR_NOMEMORY;
+ }
+
+if (result != 0)
+ {
+ *bufflenptr = pattern - pattern_start;
+ return result;
+ }
+
+*bufflenptr = out.output_size - 1;
+return 0;
+}
+
+
+/*************************************************
+* Convert pattern *
+*************************************************/
+
+/* This is the external-facing function for converting other forms of pattern
+into PCRE2 regular expression patterns. On error, the bufflenptr argument is
+used to return an offset in the original pattern.
+
+Arguments:
+ pattern the input pattern
+ plength length of input, or PCRE2_ZERO_TERMINATED
+ options options bits
+ buffptr pointer to pointer to output buffer
+ bufflenptr pointer to length of output buffer
+ ccontext convert context or NULL
+
+Returns: 0 for success, else an error code (+ve or -ve)
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options,
+ PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr,
+ pcre2_convert_context *ccontext)
+{
+int i, rc;
+PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE];
+PCRE2_UCHAR *use_buffer = dummy_buffer;
+PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE;
+BOOL utf = (options & PCRE2_CONVERT_UTF) != 0;
+uint32_t pattype = options & TYPE_OPTIONS;
+
+if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL;
+if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */
+ (pattype & (~pattype+1)) != pattype || /* More than one type set */
+ pattype == 0) /* No type set */
+ {
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_BADOPTION;
+ }
+
+if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern);
+if (ccontext == NULL) ccontext =
+ (pcre2_convert_context *)(&PRIV(default_convert_context));
+
+/* Check UTF if required. */
+
+#ifndef SUPPORT_UNICODE
+if (utf) return PCRE2_ERROR_UNICODE_NOT_SUPPORTED;
+#else
+if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0)
+ {
+ PCRE2_SIZE erroroffset;
+ rc = PRIV(valid_utf)(pattern, plength, &erroroffset);
+ if (rc != 0)
+ {
+ *bufflenptr = erroroffset;
+ return rc;
+ }
+ }
+#endif
+
+/* If buffptr is not NULL, and what it points to is not NULL, we are being
+provided with a buffer and a length, so set them as the buffer to use. */
+
+if (buffptr != NULL && *buffptr != NULL)
+ {
+ use_buffer = *buffptr;
+ use_length = *bufflenptr;
+ }
+
+/* Call an individual converter, either just once (if a buffer was provided or
+just the length is needed), or twice (if a memory allocation is required). */
+
+for (i = 0; i < 2; i++)
+ {
+ PCRE2_UCHAR *allocated;
+ BOOL dummyrun = buffptr == NULL || *buffptr == NULL;
+
+ switch(pattype)
+ {
+ case PCRE2_CONVERT_GLOB:
+ rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf,
+ use_buffer, use_length, bufflenptr, dummyrun, ccontext);
+ break;
+
+ case PCRE2_CONVERT_POSIX_BASIC:
+ case PCRE2_CONVERT_POSIX_EXTENDED:
+ rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length,
+ bufflenptr, dummyrun, ccontext);
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (rc != 0 || /* Error */
+ buffptr == NULL || /* Just the length is required */
+ *buffptr != NULL) /* Buffer was provided or allocated */
+ return rc;
+
+ /* Allocate memory for the buffer, with hidden space for an allocator at
+ the start. The next time round the loop runs the conversion for real. */
+
+ allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+ (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext);
+ if (allocated == NULL) return PCRE2_ERROR_NOMEMORY;
+ *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl));
+
+ use_buffer = *buffptr;
+ use_length = *bufflenptr + 1;
+ }
+
+/* Control should never get here. */
+
+return PCRE2_ERROR_INTERNAL;
+}
+
+
+/*************************************************
+* Free converted pattern *
+*************************************************/
+
+/* This frees a converted pattern that was put in newly-allocated memory.
+
+Argument: the converted pattern
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_converted_pattern_free(PCRE2_UCHAR *converted)
+{
+if (converted != NULL)
+ {
+ pcre2_memctl *memctl =
+ (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+/* End of pcre2_convert.c */
diff --git a/thirdparty/pcre2/src/pcre2_dfa_match.c b/thirdparty/pcre2/src/pcre2_dfa_match.c
index c909d61285..c6184ff5e9 100644
--- a/thirdparty/pcre2/src/pcre2_dfa_match.c
+++ b/thirdparty/pcre2/src/pcre2_dfa_match.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -83,7 +83,7 @@ in others, so I abandoned this code. */
#include "pcre2_internal.h"
#define PUBLIC_DFA_MATCH_OPTIONS \
- (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART)
@@ -172,7 +172,7 @@ static const uint8_t coptable[] = {
0, /* Assert not */
0, /* Assert behind */
0, /* Assert behind not */
- 0, 0, /* ONCE, ONCE_NC */
+ 0, /* ONCE */
0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
0, 0, /* CREF, DNCREF */
@@ -245,7 +245,7 @@ static const uint8_t poptable[] = {
0, /* Assert not */
0, /* Assert behind */
0, /* Assert behind not */
- 0, 0, /* ONCE, ONCE_NC */
+ 0, /* ONCE */
0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
0, 0, /* CREF, DNCREF */
@@ -294,6 +294,66 @@ typedef struct stateblock {
/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called to perform a callout.
+
+Arguments:
+ code current code pointer
+ offsets points to current capture offsets
+ current_subject start of current subject match
+ ptr current position in subject
+ mb the match block
+ extracode extra code offset when called from condition
+ lengthptr where to return the callout length
+
+Returns: the return from the callout
+*/
+
+static int
+do_callout(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject,
+ PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode,
+ PCRE2_SIZE *lengthptr)
+{
+pcre2_callout_block *cb = mb->cb;
+
+*lengthptr = (code[extracode] == OP_CALLOUT)?
+ (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] :
+ (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode);
+
+if (mb->callout == NULL) return 0; /* No callout provided */
+
+/* Fixed fields in the callout block are set once and for all at the start of
+matching. */
+
+cb->offset_vector = offsets;
+cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject);
+cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject);
+cb->pattern_position = GET(code, 1 + extracode);
+cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode);
+
+if (code[extracode] == OP_CALLOUT)
+ {
+ cb->callout_number = code[1 + 2*LINK_SIZE + extracode];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode);
+ cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1;
+ cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+return (mb->callout)(cb, mb->callout_data);
+}
+
+
+
+/*************************************************
* Match a Regular Expression - DFA engine *
*************************************************/
@@ -375,14 +435,10 @@ internal_dfa_match(
{
stateblock *active_states, *new_states, *temp_states;
stateblock *next_active_state, *next_new_state;
-
const uint8_t *ctypes, *lcc, *fcc;
PCRE2_SPTR ptr;
PCRE2_SPTR end_code;
-PCRE2_SPTR first_op;
-
dfa_recursion_info new_recursive;
-
int active_count, new_count, match_count;
/* Some fields in the mb block are frequently referenced, so we load them into
@@ -400,7 +456,8 @@ BOOL utf = FALSE;
BOOL reset_could_continue = FALSE;
-if (rlevel++ > mb->match_limit_recursion) return PCRE2_ERROR_RECURSIONLIMIT;
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
offsetcount &= (uint32_t)(-2); /* Round down */
wscount -= 2;
@@ -417,21 +474,15 @@ active_states = (stateblock *)(workspace + 2);
next_new_state = new_states = active_states + wscount;
new_count = 0;
-first_op = this_start_code + 1 + LINK_SIZE +
- ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA ||
- *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS)
- ? IMM2_SIZE:0);
-
/* The first thing in any (sub) pattern is a bracket of some sort. Push all
the alternative states onto the list, and find out where the end is. This
makes is possible to use this function recursively, when we want to stop at a
matching internal ket rather than at the end.
-If the first opcode in the first alternative is OP_REVERSE, we are dealing with
-a backward assertion. In that case, we have to find out the maximum amount to
-move back, and set up each alternative appropriately. */
+If we are dealing with a backward assertion we have to find out the maximum
+amount to move back, and set up each alternative appropriately. */
-if (*first_op == OP_REVERSE)
+if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT)
{
size_t max_back = 0;
size_t gone_back;
@@ -457,7 +508,8 @@ if (*first_op == OP_REVERSE)
{
if (current_subject <= start_subject) break;
current_subject--;
- ACROSSCHAR(current_subject > start_subject, *current_subject, current_subject--);
+ ACROSSCHAR(current_subject > start_subject, current_subject,
+ current_subject--);
}
}
else
@@ -476,15 +528,17 @@ if (*first_op == OP_REVERSE)
if (current_subject < mb->start_used_ptr)
mb->start_used_ptr = current_subject;
- /* Now we can process the individual branches. */
+ /* Now we can process the individual branches. There will be an OP_REVERSE at
+ the start of each branch, except when the length of the branch is zero. */
end_code = this_start_code;
do
{
- size_t back = (size_t)GET(end_code, 2+LINK_SIZE);
+ uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0;
+ size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE);
if (back <= gone_back)
{
- int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE);
+ int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen);
ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back));
}
end_code += GET(end_code, 1);
@@ -697,7 +751,7 @@ for (;;)
case OP_TABLE_LENGTH +
((sizeof(coptable) == OP_TABLE_LENGTH) &&
(sizeof(poptable) == OP_TABLE_LENGTH)):
- break;
+ return 0;
/* ========================================================================== */
/* Reached a closing bracket. If not at the end of the pattern, carry
@@ -1371,25 +1425,14 @@ for (;;)
if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
if (clen > 0)
{
- uint32_t lgb, rgb;
- PCRE2_SPTR nptr = ptr + clen;
int ncount = 0;
if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS)
{
active_count--; /* Remove non-match possibility */
next_active_state--;
}
- lgb = UCD_GRAPHBREAK(c);
- while (nptr < end_subject)
- {
- dlen = 1;
- if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); }
- rgb = UCD_GRAPHBREAK(d);
- if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
- ncount++;
- lgb = rgb;
- nptr += dlen;
- }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
count++;
ADD_NEW_DATA(-state_offset, count, ncount);
}
@@ -1632,8 +1675,6 @@ for (;;)
ADD_ACTIVE(state_offset + 2, 0);
if (clen > 0)
{
- uint32_t lgb, rgb;
- PCRE2_SPTR nptr = ptr + clen;
int ncount = 0;
if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR ||
codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY)
@@ -1641,17 +1682,8 @@ for (;;)
active_count--; /* Remove non-match possibility */
next_active_state--;
}
- lgb = UCD_GRAPHBREAK(c);
- while (nptr < end_subject)
- {
- dlen = 1;
- if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); }
- rgb = UCD_GRAPHBREAK(d);
- if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
- ncount++;
- lgb = rgb;
- nptr += dlen;
- }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
ADD_NEW_DATA(-(state_offset + count), 0, ncount);
}
break;
@@ -1904,25 +1936,15 @@ for (;;)
count = current_state->count; /* Number already matched */
if (clen > 0)
{
- uint32_t lgb, rgb;
- PCRE2_SPTR nptr = ptr + clen;
+ PCRE2_SPTR nptr;
int ncount = 0;
if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO)
{
active_count--; /* Remove non-match possibility */
next_active_state--;
}
- lgb = UCD_GRAPHBREAK(c);
- while (nptr < end_subject)
- {
- dlen = 1;
- if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); }
- rgb = UCD_GRAPHBREAK(d);
- if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
- ncount++;
- lgb = rgb;
- nptr += dlen;
- }
+ nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
reset_could_continue = TRUE;
if (++count >= (int)GET2(code, 1))
@@ -2099,20 +2121,9 @@ for (;;)
case OP_EXTUNI:
if (clen > 0)
{
- uint32_t lgb, rgb;
- PCRE2_SPTR nptr = ptr + clen;
int ncount = 0;
- lgb = UCD_GRAPHBREAK(c);
- while (nptr < end_subject)
- {
- dlen = 1;
- if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); }
- rgb = UCD_GRAPHBREAK(d);
- if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
- ncount++;
- lgb = rgb;
- nptr += dlen;
- }
+ PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject,
+ end_subject, utf, &ncount);
if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
reset_could_continue = TRUE;
ADD_NEW_DATA(-(state_offset + 1), 0, ncount);
@@ -2136,6 +2147,7 @@ for (;;)
case 0x2029:
#endif /* Not EBCDIC */
if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ /* Fall through */
case CHAR_LF:
ADD_NEW(state_offset + 1, 0);
@@ -2225,7 +2237,7 @@ for (;;)
case OP_NOTI:
if (clen > 0)
{
- unsigned int otherd;
+ uint32_t otherd;
#ifdef SUPPORT_UNICODE
if (utf && d >= 128)
otherd = UCD_OTHERCASE(d);
@@ -2539,11 +2551,13 @@ for (;;)
if (isinclass)
{
int max = (int)GET2(ecode, 1 + IMM2_SIZE);
- if (*ecode == OP_CRPOSRANGE)
+
+ if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1))
{
active_count--; /* Remove non-match possibility */
next_active_state--;
}
+
if (++count >= max && max != 0) /* Max 0 => no limit */
{ ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); }
else
@@ -2613,45 +2627,10 @@ for (;;)
if (code[LINK_SIZE + 1] == OP_CALLOUT
|| code[LINK_SIZE + 1] == OP_CALLOUT_STR)
{
- PCRE2_SIZE callout_length = (code[LINK_SIZE + 1] == OP_CALLOUT)?
- (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] :
- (PCRE2_SIZE)GET(code, 2 + 3*LINK_SIZE);
-
- rrc = 0;
- if (mb->callout != NULL)
- {
- pcre2_callout_block cb;
- cb.version = 1;
- cb.capture_top = 1;
- cb.capture_last = 0;
- cb.offset_vector = offsets;
- cb.mark = NULL; /* No (*MARK) support */
- cb.subject = start_subject;
- cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject);
- cb.start_match = (PCRE2_SIZE)(current_subject - start_subject);
- cb.current_position = (PCRE2_SIZE)(ptr - start_subject);
- cb.pattern_position = GET(code, LINK_SIZE + 2);
- cb.next_item_length = GET(code, LINK_SIZE + 2 + LINK_SIZE);
-
- if (code[LINK_SIZE + 1] == OP_CALLOUT)
- {
- cb.callout_number = code[2 + 3*LINK_SIZE];
- cb.callout_string_offset = 0;
- cb.callout_string = NULL;
- cb.callout_string_length = 0;
- }
- else
- {
- cb.callout_number = 0;
- cb.callout_string_offset = GET(code, 2 + 4*LINK_SIZE);
- cb.callout_string = code + (2 + 5*LINK_SIZE) + 1;
- cb.callout_string_length =
- callout_length - (1 + 4*LINK_SIZE) - 2;
- }
-
- if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0)
- return rrc; /* Abandon */
- }
+ PCRE2_SIZE callout_length;
+ rrc = do_callout(code, offsets, current_subject, ptr, mb,
+ 1 + LINK_SIZE, &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
if (rrc > 0) break; /* Fail this thread */
code += callout_length; /* Skip callout data */
}
@@ -2889,7 +2868,6 @@ for (;;)
/*-----------------------------------------------------------------*/
case OP_ONCE:
- case OP_ONCE_NC:
{
PCRE2_SIZE local_offsets[2];
int local_workspace[1000];
@@ -2984,44 +2962,10 @@ for (;;)
case OP_CALLOUT:
case OP_CALLOUT_STR:
{
- unsigned int callout_length = (*code == OP_CALLOUT)
- ? PRIV(OP_lengths)[OP_CALLOUT] : GET(code, 1 + 2*LINK_SIZE);
- rrc = 0;
-
- if (mb->callout != NULL)
- {
- pcre2_callout_block cb;
- cb.version = 1;
- cb.capture_top = 1;
- cb.capture_last = 0;
- cb.offset_vector = offsets;
- cb.mark = NULL; /* No (*MARK) support */
- cb.subject = start_subject;
- cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject);
- cb.start_match = (PCRE2_SIZE)(current_subject - start_subject);
- cb.current_position = (PCRE2_SIZE)(ptr - start_subject);
- cb.pattern_position = GET(code, 1);
- cb.next_item_length = GET(code, 1 + LINK_SIZE);
-
- if (*code == OP_CALLOUT)
- {
- cb.callout_number = code[1 + 2*LINK_SIZE];
- cb.callout_string_offset = 0;
- cb.callout_string = NULL;
- cb.callout_string_length = 0;
- }
- else
- {
- cb.callout_number = 0;
- cb.callout_string_offset = GET(code, 1 + 3*LINK_SIZE);
- cb.callout_string = code + (1 + 4*LINK_SIZE) + 1;
- cb.callout_string_length =
- callout_length - (1 + 4*LINK_SIZE) - 2;
- }
-
- if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0)
- return rrc; /* Abandon */
- }
+ PCRE2_SIZE callout_length;
+ rrc = do_callout(code, offsets, current_subject, ptr, mb, 0,
+ &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
if (rrc == 0)
{ ADD_ACTIVE(state_offset + (int)callout_length, 0); }
}
@@ -3069,7 +3013,7 @@ for (;;)
)
)
match_count = PCRE2_ERROR_PARTIAL;
- break; /* In effect, "return", but see the comment below */
+ break; /* Exit from loop along the subject string */
}
/* One or more states are active for the next character. */
@@ -3077,11 +3021,13 @@ for (;;)
ptr += clen; /* Advance to next subject character */
} /* Loop to move along the subject string */
-/* Control gets here from "break" a few lines above. We do it this way because
-if we use "return" above, we have compiler trouble. Some compilers warn if
-there's nothing here because they think the function doesn't return a value. On
-the other hand, if we put a dummy statement here, some more clever compilers
-complain that it can't be reached. Sigh. */
+/* Control gets here from "break" a few lines above. If we have a match and
+PCRE2_ENDANCHORED is set, the match fails. */
+
+if (match_count >= 0 &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 &&
+ ptr < end_subject)
+ match_count = PCRE2_ERROR_NOMATCH;
return match_count;
}
@@ -3138,6 +3084,7 @@ const uint8_t *start_bits = NULL;
/* We need to have mb pointing to a match block, because the IS_NEWLINE macro
is used below, and it expects NLBLOCK to be defined as a pointer. */
+pcre2_callout_block cb;
dfa_match_block actual_match_block;
dfa_match_block *mb = &actual_match_block;
@@ -3154,6 +3101,13 @@ if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL)
if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE;
if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
/* Check that the first field in the block is the magic number. If it is not,
return with PCRE2_ERROR_BADMAGIC. */
@@ -3208,15 +3162,28 @@ startline = (re->flags & PCRE2_STARTLINE) != 0;
firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
bumpalong_limit = end_subject;
-/* Get data from the match context, if present, and fill in the fields in the
-match block. It is an error to set an offset limit without setting the flag at
-compile time. */
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+cb.capture_top = 1; /* No capture support */
+cb.capture_last = 0;
+cb.mark = NULL; /* No (*MARK) support */
+
+/* Get data from the match context, if present, and fill in the remaining
+fields in the match block. It is an error to set an offset limit without
+setting the flag at compile time. */
if (mcontext == NULL)
{
mb->callout = NULL;
mb->memctl = re->memctl;
- mb->match_limit_recursion = PRIV(default_match_context).recursion_limit;
+ mb->match_limit = PRIV(default_match_context).match_limit;
+ mb->match_limit_depth = PRIV(default_match_context).depth_limit;
}
else
{
@@ -3229,10 +3196,15 @@ else
mb->callout = mcontext->callout;
mb->callout_data = mcontext->callout_data;
mb->memctl = mcontext->memctl;
- mb->match_limit_recursion = mcontext->recursion_limit;
+ mb->match_limit = mcontext->match_limit;
+ mb->match_limit_depth = mcontext->depth_limit;
}
-if (mb->match_limit_recursion > re->limit_recursion)
- mb->match_limit_recursion = re->limit_recursion;
+
+if (mb->match_limit > re->limit_match)
+ mb->match_limit = re->limit_match;
+
+if (mb->match_limit_depth > re->limit_depth)
+ mb->match_limit_depth = re->limit_depth;
mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
re->name_count * re->name_entry_size;
@@ -3242,6 +3214,7 @@ mb->end_subject = end_subject;
mb->start_offset = start_offset;
mb->moptions = options;
mb->poptions = re->overall_options;
+mb->match_call_count = 0;
/* Process the \R and newline settings. */
@@ -3259,6 +3232,11 @@ switch(re->newline_convention)
mb->nl[0] = CHAR_NL;
break;
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
case PCRE2_NEWLINE_CRLF:
mb->nllen = 2;
mb->nl[0] = CHAR_CR;
@@ -3325,34 +3303,27 @@ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
}
#endif /* SUPPORT_UNICODE */
-/* Set up the first code unit to match, if available. The first_codeunit value
-is never set for an anchored regular expression, but the anchoring may be
-forced at run time, so we have to test for anchoring. The first code unit may
-be unset for an unanchored pattern, of course. If there's no first code unit
-there may be a bitmap of possible first characters. */
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
-if (!anchored)
+if ((re->flags & PCRE2_FIRSTSET) != 0)
{
- if ((re->flags & PCRE2_FIRSTSET) != 0)
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
{
- has_first_cu = TRUE;
- first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
- if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
- {
- first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu);
+ first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu);
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8
- if (utf && first_cu > 127)
- first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
+ if (utf && first_cu > 127)
+ first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
#endif
- }
}
- else
- if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
- start_bits = re->start_bitmap;
}
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
-/* For anchored or unanchored matches, there may be a "last known required
-character" set. */
+/* There may be a "last known required code unit" set. */
if ((re->flags & PCRE2_LASTSET) != 0)
{
@@ -3393,13 +3364,13 @@ for (;;)
if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 &&
(options & PCRE2_DFA_RESTART) == 0)
{
- PCRE2_SPTR save_end_subject = end_subject;
-
/* If firstline is TRUE, the start of the match is constrained to the first
line of a multiline string. That is, the match must be before or at the
- first newline. Implement this by temporarily adjusting end_subject so that
- we stop the optimization scans at a newline. If the match fails at the
- newline, later code breaks this loop. */
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the optimization scans for a first code unit
+ immediately after the first character of a newline (the first code unit can
+ legitimately be a newline). If the match fails at the newline, later code
+ breaks this loop. */
if (firstline)
{
@@ -3407,85 +3378,162 @@ for (;;)
#ifdef SUPPORT_UNICODE
if (utf)
{
- while (t < mb->end_subject && !IS_NEWLINE(t))
+ while (t < end_subject && !IS_NEWLINE(t))
{
t++;
- ACROSSCHAR(t < end_subject, *t, t++);
+ ACROSSCHAR(t < end_subject, t, t++);
}
}
else
#endif
- while (t < mb->end_subject && !IS_NEWLINE(t)) t++;
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
end_subject = t;
}
- /* Advance to a unique first code unit if there is one. */
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
- if (has_first_cu)
+ if (anchored)
{
- PCRE2_UCHAR smc;
- if (first_cu != first_cu2)
- while (start_match < end_subject &&
- (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2)
- start_match++;
- else
- while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu)
- start_match++;
+ if (has_first_cu || start_bits != NULL)
+ {
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ ok = (start_bits[c/8] & (1 << (c&7))) != 0;
+ }
+ }
+ if (!ok) break;
+ }
}
- /* Or to just after a linebreak for a multiline match */
+ /* Not anchored. Advance to a unique first code unit if there is one. In
+ 8-bit mode, the use of memchr() gives a big speed up, even though we have
+ to call it twice in caseless mode, in order to find the earliest occurrence
+ of the character in either of its cases. */
- else if (startline)
+ else
{
- if (start_match > mb->start_subject + start_offset)
+ if (has_first_cu)
{
-#ifdef SUPPORT_UNICODE
- if (utf)
+ if (first_cu != first_cu2) /* Caseless */
{
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
start_match++;
- ACROSSCHAR(start_match < end_subject, *start_match,
- start_match++);
- }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp1 =
+ memchr(start_match, first_cu, end_subject-start_match);
+ PCRE2_SPTR pp2 =
+ memchr(start_match, first_cu2, end_subject-start_match);
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+#endif
}
+
+ /* The caseful case */
+
else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
#endif
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- start_match++;
+ }
- /* If we have just passed a CR and the newline option is ANY or
- ANYCRLF, and we are now at a LF, advance the match position by one more
- code unit. */
+ /* If we can't find the required code unit, having reached the true end
+ of the subject, break the bumpalong loop, to force a match failure,
+ except when doing partial matching, when we let the next cycle run at
+ the end of the subject. To see why, consider the pattern /(?<=abc)def/,
+ which partially matches "abc", even though the string does not contain
+ the starting character "d". If we have not reached the true end of the
+ subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified)
+ we also let the cycle run, because the matching string is legitimately
+ allowed to start with the first code unit of a newline. */
- if (start_match[-1] == CHAR_CR &&
- (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
- start_match < end_subject &&
- UCHAR21TEST(start_match) == CHAR_NL)
- start_match++;
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
}
- }
- /* Or to a non-unique first code unit if any have been identified. The
- bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all
- code units greater than 254 set the 255 bit. */
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
- else if (start_bits != NULL)
- {
- while (start_match < end_subject)
+ else if (startline)
{
- uint32_t c = UCHAR21TEST(start_match);
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
+ }
+
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
#if PCRE2_CODE_UNIT_WIDTH != 8
- if (c > 255) c = 255;
+ if (c > 255) c = 255;
#endif
- if ((start_bits[c/8] & (1 << (c&7))) != 0) break;
- start_match++;
+ if ((start_bits[c/8] & (1 << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next line. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
}
- }
+ } /* End of first code unit handling */
/* Restore fudged end_subject */
- end_subject = save_end_subject;
+ end_subject = mb->end_subject;
/* The following two optimizations are disabled for partial matching. */
@@ -3600,8 +3648,7 @@ for (;;)
#ifdef SUPPORT_UNICODE
if (utf)
{
- ACROSSCHAR(start_match < end_subject, *start_match,
- start_match++);
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
}
#endif
if (start_match > end_subject) break;
diff --git a/thirdparty/pcre2/src/pcre2_error.c b/thirdparty/pcre2/src/pcre2_error.c
index 437bdfd202..d98cae9963 100644
--- a/thirdparty/pcre2/src/pcre2_error.c
+++ b/thirdparty/pcre2/src/pcre2_error.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -176,6 +176,8 @@ static const unsigned char compile_error_texts[] =
"internal error: unknown code in parsed pattern\0"
/* 90 */
"internal error: bad code value in parsed_skip()\0"
+ "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0"
+ "invalid option bits with PCRE2_LITERAL\0"
;
/* Match-time and UTF error texts are in the same format. */
@@ -244,7 +246,7 @@ static const unsigned char match_error_texts[] =
"non-unique substring name\0"
"NULL argument passed\0"
"nested recursion at the same subject position\0"
- "recursion limit exceeded\0"
+ "matching depth limit exceeded\0"
"requested value is not available\0"
/* 55 */
"requested value is not set\0"
@@ -256,6 +258,8 @@ static const unsigned char match_error_texts[] =
"match with end before start is not supported\0"
"too many replacements (more than INT_MAX)\0"
"bad serialized data\0"
+ "heap limit exceeded\0"
+ "invalid syntax\0"
;
@@ -271,7 +275,7 @@ distinct.
Arguments:
enumber error number
buffer where to put the message (zero terminated)
- size size of the buffer
+ size size of the buffer in code units
Returns: length of message if all is well
negative on error
@@ -304,8 +308,8 @@ else /* Invalid error number */
for (; n > 0; n--)
{
- while (*message++ != CHAR_NULL) {};
- if (*message == CHAR_NULL) return PCRE2_ERROR_BADDATA;
+ while (*message++ != CHAR_NUL) {};
+ if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA;
}
for (i = 0; *message != 0; i++)
diff --git a/thirdparty/pcre2/src/pcre2_extuni.c b/thirdparty/pcre2/src/pcre2_extuni.c
new file mode 100644
index 0000000000..11a0bfbdd6
--- /dev/null
+++ b/thirdparty/pcre2/src/pcre2_extuni.c
@@ -0,0 +1,148 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains an internal function that is used to match a Unicode
+extended grapheme sequence. It is used by both pcre2_match() and
+pcre2_def_match(). However, it is called only when Unicode support is being
+compiled. Nevertheless, we provide a dummy function when there is no Unicode
+support, because some compilers do not like functionless source files. */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "pcre2_internal.h"
+
+
+/* Dummy function */
+
+#ifndef SUPPORT_UNICODE
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+(void)c;
+(void)eptr;
+(void)start_subject;
+(void)end_subject;
+(void)utf;
+(void)xcount;
+return NULL;
+}
+#else
+
+
+/*************************************************
+* Match an extended grapheme sequence *
+*************************************************/
+
+/*
+Arguments:
+ c the first character
+ eptr pointer to next character
+ start_subject pointer to start of subject
+ end_subject pointer to end of subject
+ utf TRUE if in UTF mode
+ xcount pointer to count of additional characters,
+ or NULL if count not needed
+
+Returns: pointer after the end of the sequence
+*/
+
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+int lgb = UCD_GRAPHBREAK(c);
+
+while (eptr < end_subject)
+ {
+ int rgb;
+ int len = 1;
+ if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
+ rgb = UCD_GRAPHBREAK(c);
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator)
+ {
+ int ricount = 0;
+ PCRE2_SPTR bptr = eptr - 1;
+ if (utf) BACKCHAR(bptr);
+
+ /* bptr is pointing to the left-hand character */
+
+ while (bptr > start_subject)
+ {
+ bptr--;
+ if (utf)
+ {
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+ }
+ else
+ c = *bptr;
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break;
+ ricount++;
+ }
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend follows E_Base[_GAZ] do not update lgb; this allows
+ any number of Extend before a following E_Modifier. */
+
+ if (rgb != ucp_gbExtend ||
+ (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ))
+ lgb = rgb;
+
+ eptr += len;
+ if (xcount != NULL) *xcount += 1;
+ }
+
+return eptr;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_extuni.c */
diff --git a/thirdparty/pcre2/src/pcre2_internal.h b/thirdparty/pcre2/src/pcre2_internal.h
index 6a8774ce8c..3db9d604f4 100644
--- a/thirdparty/pcre2/src/pcre2_internal.h
+++ b/thirdparty/pcre2/src/pcre2_internal.h
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,9 @@ POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
*/
+#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+
/* We do not support both EBCDIC and Unicode at the same time. The "configure"
script prevents both being selected, but not everybody uses "configure". EBCDIC
is only supported for the 8-bit library, but the check for this has to be later
@@ -240,6 +243,16 @@ not rely on this. */
#define COMPILE_ERROR_BASE 100
+/* The initial frames vector for remembering backtracking points in
+pcre2_match() is allocated on the system stack, of this size (bytes). The size
+must be a multiple of sizeof(PCRE2_SPTR) in all environments, so making it a
+multiple of 8 is best. Typical frame sizes are a few hundred bytes (it depends
+on the number of capturing parentheses) so 20K handles quite a few frames. A
+larger vector on the heap is obtained for patterns that need more frames. The
+maximum size of this can be limited. */
+
+#define START_FRAMES_SIZE 20480
+
/* Define the default BSR convention. */
#ifdef BSR_ANYCRLF
@@ -547,9 +560,14 @@ enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */
#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
/* The maximum remaining length of subject we are prepared to search for a
-req_unit match. */
+req_unit match. In 8-bit mode, memchr() is used and is much faster than the
+search loop that has to be used in 16-bit and 32-bit modes. */
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define REQ_CU_MAX 2000
+#else
#define REQ_CU_MAX 1000
+#endif
/* Offsets for the bitmap tables in the cbits set of tables. Each table
contains a set of bits for a class map. Some classes are built by combining
@@ -668,7 +686,7 @@ a positive value. */
/* The remaining definitions work in both environments. */
-#define CHAR_NULL '\0'
+#define CHAR_NUL '\0'
#define CHAR_HT '\t'
#define CHAR_VT '\v'
#define CHAR_FF '\f'
@@ -909,6 +927,7 @@ a positive value. */
#define STRING_CRLF_RIGHTPAR "CRLF)"
#define STRING_ANY_RIGHTPAR "ANY)"
#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)"
+#define STRING_NUL_RIGHTPAR "NUL)"
#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)"
#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)"
#define STRING_UTF8_RIGHTPAR "UTF8)"
@@ -922,7 +941,9 @@ a positive value. */
#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)"
#define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)"
#define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)"
+#define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP="
#define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH="
+#define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH="
#define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION="
#define STRING_MARK "MARK"
@@ -944,7 +965,7 @@ only. */
#define CHAR_ESC '\033'
#define CHAR_DEL '\177'
-#define CHAR_NULL '\0'
+#define CHAR_NUL '\0'
#define CHAR_SPACE '\040'
#define CHAR_EXCLAMATION_MARK '\041'
#define CHAR_QUOTATION_MARK '\042'
@@ -1182,6 +1203,7 @@ only. */
#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS
#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS
#define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS
@@ -1195,7 +1217,9 @@ only. */
#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS
#define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS
#define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS
+#define STRING_LIMIT_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN
#define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN
+#define STRING_LIMIT_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN
#define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN
#define STRING_MARK STR_M STR_A STR_R STR_K
@@ -1510,68 +1534,67 @@ enum {
OP_ASSERTBACK, /* 128 Positive lookbehind */
OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */
- /* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately
- after the assertions, with ONCE first, as there's a test for >= ONCE for a
- subpattern that isn't an assertion. The POS versions must immediately follow
- the non-POS versions in each case. */
+ /* ONCE, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately after the
+ assertions, with ONCE first, as there's a test for >= ONCE for a subpattern
+ that isn't an assertion. The POS versions must immediately follow the non-POS
+ versions in each case. */
OP_ONCE, /* 130 Atomic group, contains captures */
- OP_ONCE_NC, /* 131 Atomic group containing no captures */
- OP_BRA, /* 132 Start of non-capturing bracket */
- OP_BRAPOS, /* 133 Ditto, with unlimited, possessive repeat */
- OP_CBRA, /* 134 Start of capturing bracket */
- OP_CBRAPOS, /* 135 Ditto, with unlimited, possessive repeat */
- OP_COND, /* 136 Conditional group */
+ OP_BRA, /* 131 Start of non-capturing bracket */
+ OP_BRAPOS, /* 132 Ditto, with unlimited, possessive repeat */
+ OP_CBRA, /* 133 Start of capturing bracket */
+ OP_CBRAPOS, /* 134 Ditto, with unlimited, possessive repeat */
+ OP_COND, /* 135 Conditional group */
/* These five must follow the previous five, in the same order. There's a
check for >= SBRA to distinguish the two sets. */
- OP_SBRA, /* 137 Start of non-capturing bracket, check empty */
- OP_SBRAPOS, /* 138 Ditto, with unlimited, possessive repeat */
- OP_SCBRA, /* 139 Start of capturing bracket, check empty */
- OP_SCBRAPOS, /* 140 Ditto, with unlimited, possessive repeat */
- OP_SCOND, /* 141 Conditional group, check empty */
+ OP_SBRA, /* 136 Start of non-capturing bracket, check empty */
+ OP_SBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */
+ OP_SCBRA, /* 138 Start of capturing bracket, check empty */
+ OP_SCBRAPOS, /* 139 Ditto, with unlimited, possessive repeat */
+ OP_SCOND, /* 140 Conditional group, check empty */
/* The next two pairs must (respectively) be kept together. */
- OP_CREF, /* 142 Used to hold a capture number as condition */
- OP_DNCREF, /* 143 Used to point to duplicate names as a condition */
- OP_RREF, /* 144 Used to hold a recursion number as condition */
- OP_DNRREF, /* 145 Used to point to duplicate names as a condition */
- OP_FALSE, /* 146 Always false (used by DEFINE and VERSION) */
- OP_TRUE, /* 147 Always true (used by VERSION) */
+ OP_CREF, /* 141 Used to hold a capture number as condition */
+ OP_DNCREF, /* 142 Used to point to duplicate names as a condition */
+ OP_RREF, /* 143 Used to hold a recursion number as condition */
+ OP_DNRREF, /* 144 Used to point to duplicate names as a condition */
+ OP_FALSE, /* 145 Always false (used by DEFINE and VERSION) */
+ OP_TRUE, /* 146 Always true (used by VERSION) */
- OP_BRAZERO, /* 148 These two must remain together and in this */
- OP_BRAMINZERO, /* 149 order. */
- OP_BRAPOSZERO, /* 150 */
+ OP_BRAZERO, /* 147 These two must remain together and in this */
+ OP_BRAMINZERO, /* 148 order. */
+ OP_BRAPOSZERO, /* 149 */
/* These are backtracking control verbs */
- OP_MARK, /* 151 always has an argument */
- OP_PRUNE, /* 152 */
- OP_PRUNE_ARG, /* 153 same, but with argument */
- OP_SKIP, /* 154 */
- OP_SKIP_ARG, /* 155 same, but with argument */
- OP_THEN, /* 156 */
- OP_THEN_ARG, /* 157 same, but with argument */
- OP_COMMIT, /* 158 */
+ OP_MARK, /* 150 always has an argument */
+ OP_PRUNE, /* 151 */
+ OP_PRUNE_ARG, /* 152 same, but with argument */
+ OP_SKIP, /* 153 */
+ OP_SKIP_ARG, /* 154 same, but with argument */
+ OP_THEN, /* 155 */
+ OP_THEN_ARG, /* 156 same, but with argument */
+ OP_COMMIT, /* 157 */
/* These are forced failure and success verbs */
- OP_FAIL, /* 159 */
- OP_ACCEPT, /* 160 */
- OP_ASSERT_ACCEPT, /* 161 Used inside assertions */
- OP_CLOSE, /* 162 Used before OP_ACCEPT to close open captures */
+ OP_FAIL, /* 158 */
+ OP_ACCEPT, /* 159 */
+ OP_ASSERT_ACCEPT, /* 160 Used inside assertions */
+ OP_CLOSE, /* 161 Used before OP_ACCEPT to close open captures */
/* This is used to skip a subpattern with a {0} quantifier */
- OP_SKIPZERO, /* 163 */
+ OP_SKIPZERO, /* 162 */
/* This is used to identify a DEFINE group during compilation so that it can
be checked for having only one branch. It is changed to OP_FALSE before
compilation finishes. */
- OP_DEFINE, /* 164 */
+ OP_DEFINE, /* 163 */
/* This is not an opcode, but is used to check that tables indexed by opcode
are the correct length, in order to catch updating errors - there have been
@@ -1618,7 +1641,7 @@ some cases doesn't actually use these names at all). */
"Recurse", "Callout", "CalloutStr", \
"Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \
"Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \
- "Once", "Once_NC", \
+ "Once", \
"Bra", "BraPos", "CBra", "CBraPos", \
"Cond", \
"SBra", "SBraPos", "SCBra", "SCBraPos", \
@@ -1702,7 +1725,6 @@ in UTF-8 mode. The code that uses this table must know about such things. */
1+LINK_SIZE, /* Assert behind */ \
1+LINK_SIZE, /* Assert behind not */ \
1+LINK_SIZE, /* ONCE */ \
- 1+LINK_SIZE, /* ONCE_NC */ \
1+LINK_SIZE, /* BRA */ \
1+LINK_SIZE, /* BRAPOS */ \
1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \
@@ -1748,6 +1770,7 @@ typedef struct open_capitem {
struct open_capitem *next; /* Chain link */
uint16_t number; /* Capture number */
uint16_t flag; /* Set TRUE if recursive back ref */
+ uint16_t assert_depth; /* Assertion depth when opened */
} open_capitem;
/* Layout of the UCP type table that translates property names into types and
@@ -1774,10 +1797,17 @@ typedef struct {
/* UCD access macros */
#define UCD_BLOCK_SIZE 128
-#define GET_UCD(ch) (PRIV(ucd_records) + \
+#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \
PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \
UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE])
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \
+ PRIV(dummy_ucd_record) : REAL_GET_UCD(ch))
+#else
+#define GET_UCD(ch) REAL_GET_UCD(ch)
+#endif
+
#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype
#define UCD_SCRIPT(ch) GET_UCD(ch)->script
#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)]
@@ -1832,8 +1862,12 @@ extern const uint8_t PRIV(utf8_table4)[];
#define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_)
#define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_)
#define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_)
+#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_)
#define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_)
#define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_)
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_)
+#endif
#define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_)
#define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_)
#define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_)
@@ -1852,12 +1886,16 @@ extern const uint8_t PRIV(OP_lengths)[];
extern const uint32_t PRIV(callout_end_delims)[];
extern const uint32_t PRIV(callout_start_delims)[];
extern const pcre2_compile_context PRIV(default_compile_context);
+extern const pcre2_convert_context PRIV(default_convert_context);
extern const pcre2_match_context PRIV(default_match_context);
extern const uint8_t PRIV(default_tables)[];
extern const uint32_t PRIV(hspace_list)[];
extern const uint32_t PRIV(vspace_list)[];
extern const uint32_t PRIV(ucd_caseless_sets)[];
extern const ucd_record PRIV(ucd_records)[];
+#if PCRE2_CODE_UNIT_WIDTH == 32
+extern const ucd_record PRIV(dummy_ucd_record)[];
+#endif
extern const uint8_t PRIV(ucd_stage1)[];
extern const uint16_t PRIV(ucd_stage2)[];
extern const uint32_t PRIV(ucp_gbtable)[];
@@ -1892,6 +1930,7 @@ is available. */
#define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_)
#define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_)
+#define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_)
#define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_)
#define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_)
#define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_)
@@ -1915,6 +1954,8 @@ extern int _pcre2_auto_possessify(PCRE2_UCHAR *, BOOL,
const compile_block *);
extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *,
int *, uint32_t, BOOL, compile_block *);
+extern PCRE2_SPTR _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR,
+ BOOL, int *);
extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int);
extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
uint32_t *, BOOL);
@@ -1936,5 +1977,6 @@ extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
uint32_t *, BOOL);
extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL);
#endif /* PCRE2_CODE_UNIT_WIDTH */
+#endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */
/* End of pcre2_internal.h */
diff --git a/thirdparty/pcre2/src/pcre2_intmodedep.h b/thirdparty/pcre2/src/pcre2_intmodedep.h
index ebff7e3066..c4c4c3adb9 100644
--- a/thirdparty/pcre2/src/pcre2_intmodedep.h
+++ b/thirdparty/pcre2/src/pcre2_intmodedep.h
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -54,6 +54,7 @@ just to undefine them all. */
#undef ACROSSCHAR
#undef BACKCHAR
#undef BYTES2CU
+#undef CHMAX_255
#undef CU2BYTES
#undef FORWARDCHAR
#undef FORWARDCHARTEST
@@ -201,20 +202,25 @@ arithmetic results in a signed value. Hence the cast. */
/* Other macros that are different for 8-bit mode. The MAX_255 macro checks
whether its argument, which is assumed to be one code unit, is less than 256.
-The maximum length of a MARK name must fit in one code unit; currently it is
-set to 255 or 65535. The TABLE_GET macro is used to access elements of tables
-containing exactly 256 items. When code points can be greater than 255, a check
-is needed before accessing these tables. */
+The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK
+name must fit in one code unit; currently it is set to 255 or 65535. The
+TABLE_GET macro is used to access elements of tables containing exactly 256
+items. When code points can be greater than 255, a check is needed before
+accessing these tables. */
#if PCRE2_CODE_UNIT_WIDTH == 8
#define MAX_255(c) TRUE
#define MAX_MARK ((1u << 8) - 1)
#ifdef SUPPORT_UNICODE
#define SUPPORT_WIDE_CHARS
+#define CHMAX_255(c) ((c) <= 255u)
+#else
+#define CHMAX_255(c) TRUE
#endif /* SUPPORT_UNICODE */
#define TABLE_GET(c, table, default) ((table)[c])
#else /* Code units are 16 or 32 bits */
+#define CHMAX_255(c) ((c) <= 255u)
#define MAX_255(c) ((c) <= 255u)
#define MAX_MARK ((1u << 16) - 1)
#define SUPPORT_WIDE_CHARS
@@ -345,7 +351,7 @@ because almost all calls are already within a block of UTF-8 only code. */
/* Same as above, but it allows a fully customizable form. */
#define ACROSSCHAR(condition, eptr, action) \
- while((condition) && ((eptr) & 0xc0u) == 0x80u) action
+ while((condition) && ((*eptr) & 0xc0u) == 0x80u) action
/* Deposit a character into memory, returning the number of code units. */
@@ -451,7 +457,7 @@ code. */
/* Same as above, but it allows a fully customizable form. */
#define ACROSSCHAR(condition, eptr, action) \
- if ((condition) && ((eptr) & 0xfc00u) == 0xdc00u) action
+ if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action
/* Deposit a character into memory, returning the number of code units. */
@@ -566,15 +572,13 @@ typedef struct pcre2_real_compile_context {
uint16_t bsr_convention;
uint16_t newline_convention;
uint32_t parens_nest_limit;
+ uint32_t extra_options;
} pcre2_real_compile_context;
/* The real match context structure. */
typedef struct pcre2_real_match_context {
pcre2_memctl memctl;
-#ifdef HEAP_MATCH_RECURSE
- pcre2_memctl stack_memctl;
-#endif
#ifdef SUPPORT_JIT
pcre2_jit_callback jit_callback;
void *jit_callback_data;
@@ -582,10 +586,19 @@ typedef struct pcre2_real_match_context {
int (*callout)(pcre2_callout_block *, void *);
void *callout_data;
PCRE2_SIZE offset_limit;
+ uint32_t heap_limit;
uint32_t match_limit;
- uint32_t recursion_limit;
+ uint32_t depth_limit;
} pcre2_real_match_context;
+/* The real convert context structure. */
+
+typedef struct pcre2_real_convert_context {
+ pcre2_memctl memctl;
+ uint32_t glob_separator;
+ uint32_t glob_escape;
+} pcre2_real_convert_context;
+
/* The real compiled code structure. The type for the blocksize field is
defined specially because it is required in pcre2_serialize_decode() when
copying the size from possibly unaligned memory into a variable of the same
@@ -610,9 +623,11 @@ typedef struct pcre2_real_code {
uint32_t magic_number; /* Paranoid and endianness check */
uint32_t compile_options; /* Options passed to pcre2_compile() */
uint32_t overall_options; /* Options after processing the pattern */
+ uint32_t extra_options; /* Taken from compile_context */
uint32_t flags; /* Various state flags */
+ uint32_t limit_heap; /* Limit set in the pattern */
uint32_t limit_match; /* Limit set in the pattern */
- uint32_t limit_recursion; /* Limit set in the pattern */
+ uint32_t limit_depth; /* Limit set in the pattern */
uint32_t first_codeunit; /* Starting code unit */
uint32_t last_codeunit; /* This codeunit must be seen */
uint16_t bsr_convention; /* What \R matches */
@@ -625,7 +640,13 @@ typedef struct pcre2_real_code {
uint16_t name_count; /* Number of name entries in the table */
} pcre2_real_code;
-/* The real match data structure. */
+/* The real match data structure. Define ovector as large as it can ever
+actually be so that array bound checkers don't grumble. Memory for this
+structure is obtained by calling pcre2_match_data_create(), which sets the size
+as the offset of ovector plus a pair of elements for each capturable string, so
+the size varies from call to call. As the maximum number of capturing
+subpatterns is 65535 we must allow for 65536 strings to include the overall
+match. (See also the heapframe structure below.) */
typedef struct pcre2_real_match_data {
pcre2_memctl memctl;
@@ -638,7 +659,7 @@ typedef struct pcre2_real_match_data {
uint16_t matchedby; /* Type of match (normal, JIT, DFA) */
uint16_t oveccount; /* Number of pairs */
int rc; /* The return code from the match */
- PCRE2_SIZE ovector[1]; /* The first field */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
} pcre2_real_match_data;
@@ -705,6 +726,8 @@ typedef struct compile_block {
PCRE2_SIZE erroroffset; /* Offset of error in pattern */
uint16_t names_found; /* Number of entries so far */
uint16_t name_entry_size; /* Size of each entry */
+ uint16_t parens_depth; /* Depth of nested parentheses */
+ uint16_t assert_depth; /* Depth of nested assertions */
open_capitem *open_caps; /* Chain of open capture items */
named_group *named_groups; /* Points to vector in pre-compile */
uint32_t named_group_list_size; /* Number of entries in the list */
@@ -723,8 +746,6 @@ typedef struct compile_block {
uint32_t class_range_end; /* Overall class range end */
PCRE2_UCHAR nl[4]; /* Newline string when fixed length */
int max_lookbehind; /* Maximum lookbehind (characters) */
- int parens_depth; /* Depth of nested parentheses */
- int assert_depth; /* Depth of nested assertions */
int req_varyopt; /* "After variable item" flag for reqbyte */
BOOL had_accept; /* (*ACCEPT) encountered */
BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */
@@ -740,27 +761,8 @@ typedef struct pcre2_real_jit_stack {
void* stack;
} pcre2_real_jit_stack;
-/* Structure for keeping a chain of heap blocks used for saving ovectors
-during pattern recursion when the ovector is larger than can be saved on
-the system stack. */
-
-typedef struct ovecsave_frame {
- struct ovecsave_frame *next; /* Next frame on free chain */
- PCRE2_SIZE saved_ovec[1]; /* First vector element */
-} ovecsave_frame;
-
/* Structure for items in a linked list that represents an explicit recursive
-call within the pattern; used by pcre_match(). */
-
-typedef struct recursion_info {
- struct recursion_info *prevrec; /* Previous recursion record (or NULL) */
- unsigned int group_num; /* Number of group that was called */
- PCRE2_SIZE *ovec_save; /* Pointer to saved ovector frame */
- uint32_t saved_capture_last; /* Last capture number */
- PCRE2_SPTR subject_position; /* Position at start of recursion */
-} recursion_info;
-
-/* A similar structure for pcre_dfa_match(). */
+call within the pattern when running pcre_dfa_match(). */
typedef struct dfa_recursion_info {
struct dfa_recursion_info *prevrec;
@@ -768,35 +770,75 @@ typedef struct dfa_recursion_info {
uint32_t group_num;
} dfa_recursion_info;
-/* Structure for building a chain of data for holding the values of the subject
-pointer at the start of each subpattern, so as to detect when an empty string
-has been matched by a subpattern - to break infinite loops; used by
-pcre2_match(). */
+/* Structure for "stack" frames that are used for remembering backtracking
+positions during matching. As these are used in a vector, with the ovector item
+being extended, the size of the structure must be a multiple of PCRE2_SIZE. The
+only way to check this at compile time is to force an error by generating an
+array with a negative size. By putting this in a typedef (which is never used),
+we don't generate any code when all is well. */
+
+typedef struct heapframe {
+
+ /* The first set of fields are variables that have to be preserved over calls
+ to RRMATCH(), but which do not need to be copied to new frames. */
+
+ PCRE2_SPTR ecode; /* The current position in the pattern */
+ PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */
+ PCRE2_SIZE length; /* Used for character, string, or code lengths */
+ PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */
+ PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */
+ uint32_t rdepth; /* "Recursion" depth */
+ uint32_t group_frame_type; /* Type information for group frames */
+ uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */
+ uint8_t return_id; /* Where to go on in internal "return" */
+ uint8_t op; /* Processing opcode */
-typedef struct eptrblock {
- struct eptrblock *epb_prev;
- PCRE2_SPTR epb_saved_eptr;
-} eptrblock;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ PCRE2_UCHAR occu[6]; /* Used for other case code units */
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ PCRE2_UCHAR occu[2]; /* Used for other case code units */
+#else
+ PCRE2_UCHAR occu[1]; /* Used for other case code units */
+#endif
+
+ /* The rest have to be copied from the previous frame whenever a new frame
+ becomes current. The final field is specified as a large vector so that
+ runtime array bound checks don't catch references to it. However, for any
+ specific call to pcre2_match() the memory allocated for each frame structure
+ allows for exactly the right size ovector for the number of capturing
+ parentheses. (See also the comment for pcre2_real_match_data above.) */
+
+ PCRE2_SPTR eptr; /* MUST BE FIRST */
+ PCRE2_SPTR start_match; /* Can be adjusted by \K */
+ PCRE2_SPTR mark; /* Most recent mark on the success path */
+ uint32_t current_recurse; /* Current (deepest) recursion number */
+ uint32_t capture_last; /* Most recent capture */
+ PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */
+ PCRE2_SIZE offset_top; /* Offset after highest capture */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
+} heapframe;
+
+typedef char check_heapframe_size[
+ ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)];
/* Structure for passing "static" information around between the functions
doing traditional NFA matching (pcre2_match() and friends). */
typedef struct match_block {
pcre2_memctl memctl; /* For general use */
-#ifdef HEAP_MATCH_RECURSE
- pcre2_memctl stack_memctl; /* For "stack" frames */
-#endif
- uint32_t match_call_count; /* As it says */
+ PCRE2_SIZE frame_vector_size; /* Size of a backtracking frame */
+ heapframe *match_frames; /* Points to vector of frames */
+ heapframe *match_frames_top; /* Points after the end of the vector */
+ heapframe *stack_frames; /* The original vector on the stack */
+ PCRE2_SIZE heap_limit; /* As it says */
uint32_t match_limit; /* As it says */
- uint32_t match_limit_recursion; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of times a new frame is created */
BOOL hitend; /* Hit the end of the subject at some point */
BOOL hasthen; /* Pattern contains (*THEN) */
const uint8_t *lcc; /* Points to lower casing table */
const uint8_t *fcc; /* Points to case-flipping table */
const uint8_t *ctypes; /* Points to table of type maps */
- PCRE2_SIZE *ovector; /* Pointer to the offset vector */
- PCRE2_SIZE offset_end; /* One past the end */
- PCRE2_SIZE offset_max; /* The maximum usable for return data */
PCRE2_SIZE start_offset; /* The start offset value */
PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */
uint16_t partial; /* PARTIAL options */
@@ -807,30 +849,24 @@ typedef struct match_block {
PCRE2_SPTR start_code; /* For use when recursing */
PCRE2_SPTR start_subject; /* Start of the subject string */
PCRE2_SPTR end_subject; /* End of the subject string */
- PCRE2_SPTR start_match_ptr; /* Start of matched string */
PCRE2_SPTR end_match_ptr; /* Subject position at end match */
PCRE2_SPTR start_used_ptr; /* Earliest consulted character */
PCRE2_SPTR last_used_ptr; /* Latest consulted character */
PCRE2_SPTR mark; /* Mark pointer to pass back on success */
PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */
- PCRE2_SPTR once_target; /* Where to back up to for atomic groups */
+ PCRE2_SPTR verb_ecode_ptr; /* For passing back info */
+ PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */
+ uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */
uint32_t moptions; /* Match options */
uint32_t poptions; /* Pattern options */
- uint32_t capture_last; /* Most recent capture number + overflow flag */
uint32_t skip_arg_count; /* For counting SKIP_ARGs */
uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */
- uint32_t match_function_type; /* Set for certain special calls of match() */
uint32_t nltype; /* Newline type */
uint32_t nllen; /* Newline string length */
PCRE2_UCHAR nl[4]; /* Newline string when fixed */
- eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */
- recursion_info *recursive; /* Linked list of recursion data */
- ovecsave_frame *ovecsave_chain; /* Linked list of free ovecsave blocks */
+ pcre2_callout_block *cb; /* Points to a callout block */
void *callout_data; /* To pass back to callouts */
int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
-#ifdef HEAP_MATCH_RECURSE
- void *match_frames_base; /* For remembering malloc'd frames */
-#endif
} match_block;
/* A similar structure is used for the same purpose by the DFA matching
@@ -845,13 +881,16 @@ typedef struct dfa_match_block {
PCRE2_SPTR last_used_ptr; /* Latest consulted character */
const uint8_t *tables; /* Character tables */
PCRE2_SIZE start_offset; /* The start offset value */
- uint32_t match_limit_recursion; /* As it says */
+ uint32_t match_limit; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of calls of internal function */
uint32_t moptions; /* Match options */
uint32_t poptions; /* Pattern options */
uint32_t nltype; /* Newline type */
uint32_t nllen; /* Newline string length */
PCRE2_UCHAR nl[4]; /* Newline string when fixed */
uint16_t bsr_convention; /* \R interpretation */
+ pcre2_callout_block *cb; /* Points to a callout block */
void *callout_data; /* To pass back to callouts */
int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
dfa_recursion_info *recursive; /* Linked list of recursion data */
diff --git a/thirdparty/pcre2/src/pcre2_jit_compile.c b/thirdparty/pcre2/src/pcre2_jit_compile.c
index 8dea90a1c5..80ed1c4ca6 100644
--- a/thirdparty/pcre2/src/pcre2_jit_compile.c
+++ b/thirdparty/pcre2/src/pcre2_jit_compile.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -228,7 +228,7 @@ enum control_types {
type_then_trap = 1
};
-typedef int (SLJIT_CALL *jit_function)(jit_arguments *args);
+typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args);
/* The following structure is the key data type for the recursive
code generator. It is allocated by compile_matchingpath, and contains
@@ -313,16 +313,25 @@ typedef struct ref_iterator_backtrack {
typedef struct recurse_entry {
struct recurse_entry *next;
- /* Contains the function entry. */
- struct sljit_label *entry;
- /* Collects the calls until the function is not created. */
- jump_list *calls;
+ /* Contains the function entry label. */
+ struct sljit_label *entry_label;
+ /* Contains the function entry label. */
+ struct sljit_label *backtrack_label;
+ /* Collects the entry calls until the function is not created. */
+ jump_list *entry_calls;
+ /* Collects the backtrack calls until the function is not created. */
+ jump_list *backtrack_calls;
/* Points to the starting opcode. */
sljit_sw start;
} recurse_entry;
typedef struct recurse_backtrack {
backtrack_common common;
+ /* Return to the matching path. */
+ struct sljit_label *matchingpath;
+ /* Recursive pattern. */
+ recurse_entry *entry;
+ /* Pattern is inlined. */
BOOL inlined_pattern;
} recurse_backtrack;
@@ -341,11 +350,26 @@ typedef struct then_trap_backtrack {
int framesize;
} then_trap_backtrack;
-#define MAX_RANGE_SIZE 4
+#define MAX_N_CHARS 12
+#define MAX_DIFF_CHARS 5
+
+typedef struct fast_forward_char_data {
+ /* Number of characters in the chars array, 255 for any character. */
+ sljit_u8 count;
+ /* Number of last UTF-8 characters in the chars array. */
+ sljit_u8 last_count;
+ /* Available characters in the current position. */
+ PCRE2_UCHAR chars[MAX_DIFF_CHARS];
+} fast_forward_char_data;
+
+#define MAX_CLASS_RANGE_SIZE 4
+#define MAX_CLASS_CHARS_SIZE 3
typedef struct compiler_common {
/* The sljit ceneric compiler. */
struct sljit_compiler *compiler;
+ /* Compiled regular expression. */
+ pcre2_real_code *re;
/* First byte code. */
PCRE2_SPTR start;
/* Maps private data offset to each opcode. */
@@ -402,10 +426,10 @@ typedef struct compiler_common {
BOOL has_then;
/* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */
BOOL has_skip_in_assert_back;
- /* Currently in recurse or negative assert. */
- BOOL local_exit;
- /* Currently in a positive assert. */
- BOOL positive_assert;
+ /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */
+ BOOL local_quit_available;
+ /* Currently in a positive assertion. */
+ BOOL in_positive_assertion;
/* Newline control. */
int nltype;
sljit_u32 nlmax;
@@ -426,7 +450,7 @@ typedef struct compiler_common {
/* Labels and jump lists. */
struct sljit_label *partialmatchlabel;
struct sljit_label *quit_label;
- struct sljit_label *forced_quit_label;
+ struct sljit_label *abort_label;
struct sljit_label *accept_label;
struct sljit_label *ff_newline_shortcut;
stub_list *stubs;
@@ -435,8 +459,9 @@ typedef struct compiler_common {
recurse_entry *currententry;
jump_list *partialmatch;
jump_list *quit;
- jump_list *positive_assert_quit;
- jump_list *forced_quit;
+ jump_list *positive_assertion_quit;
+ jump_list *abort;
+ jump_list *failed_match;
jump_list *accept;
jump_list *calllimit;
jump_list *stackalloc;
@@ -500,14 +525,29 @@ typedef struct compare_context {
#undef CMP
/* Used for accessing the elements of the stack. */
-#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_sw))
+#define STACK(i) ((i) * (int)sizeof(sljit_sw))
+
+#ifdef SLJIT_PREF_SHIFT_REG
+#if SLJIT_PREF_SHIFT_REG == SLJIT_R2
+/* Nothing. */
+#elif SLJIT_PREF_SHIFT_REG == SLJIT_R3
+#define SHIFT_REG_IS_R3
+#else
+#error "Unsupported shift register"
+#endif
+#endif
#define TMP1 SLJIT_R0
+#ifdef SHIFT_REG_IS_R3
+#define TMP2 SLJIT_R3
+#define TMP3 SLJIT_R2
+#else
#define TMP2 SLJIT_R2
#define TMP3 SLJIT_R3
-#define STR_PTR SLJIT_S0
-#define STR_END SLJIT_S1
-#define STACK_TOP SLJIT_R1
+#endif
+#define STR_PTR SLJIT_R1
+#define STR_END SLJIT_S0
+#define STACK_TOP SLJIT_S1
#define STACK_LIMIT SLJIT_S2
#define COUNT_MATCH SLJIT_S3
#define ARGUMENTS SLJIT_S4
@@ -533,16 +573,13 @@ the start pointers when the end of the capturing group has not yet reached. */
#if PCRE2_CODE_UNIT_WIDTH == 8
#define MOV_UCHAR SLJIT_MOV_U8
-#define MOVU_UCHAR SLJIT_MOVU_U8
#define IN_UCHARS(x) (x)
#elif PCRE2_CODE_UNIT_WIDTH == 16
#define MOV_UCHAR SLJIT_MOV_U16
-#define MOVU_UCHAR SLJIT_MOVU_U16
#define UCHAR_SHIFT (1)
#define IN_UCHARS(x) ((x) * 2)
#elif PCRE2_CODE_UNIT_WIDTH == 32
#define MOV_UCHAR SLJIT_MOV_U32
-#define MOVU_UCHAR SLJIT_MOVU_U32
#define UCHAR_SHIFT (2)
#define IN_UCHARS(x) ((x) * 4)
#else
@@ -570,13 +607,17 @@ the start pointers when the end of the capturing group has not yet reached. */
sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w))
#define CMPTO(type, src1, src1w, src2, src2w, label) \
sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label))
-#define OP_FLAGS(op, dst, dstw, src, srcw, type) \
- sljit_emit_op_flags(compiler, (op), (dst), (dstw), (src), (srcw), (type))
+#define OP_FLAGS(op, dst, dstw, type) \
+ sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type))
+#define CMOV(type, dst_reg, src, srcw) \
+ sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw))
#define GET_LOCAL_BASE(dst, dstw, offset) \
sljit_get_local_base(compiler, (dst), (dstw), (offset))
#define READ_CHAR_MAX 0x7fffffff
+#define INVALID_UTF_CHAR 888
+
static PCRE2_SPTR bracketend(PCRE2_SPTR cc)
{
SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
@@ -606,8 +647,8 @@ return count;
set_private_data_ptrs
get_framesize
init_frame
- get_private_data_copy_length
- copy_private_data
+ get_recurse_data_length
+ copy_recurse_data
compile_matchingpath
compile_backtrackingpath
*/
@@ -675,7 +716,6 @@ switch(*cc)
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_BRAPOS:
case OP_CBRA:
@@ -806,7 +846,7 @@ switch(*cc)
default:
/* All opcodes are supported now! */
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
}
@@ -1304,7 +1344,7 @@ while (cc < ccend)
if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE)
break;
- if (repeat_check && (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND))
+ if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND))
{
if (detect_repeat(common, cc))
{
@@ -1333,7 +1373,6 @@ while (cc < ccend)
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRAPOS:
case OP_SBRA:
case OP_SBRAPOS:
@@ -1654,11 +1693,11 @@ if (length > 0)
return stack_restore ? no_frame : no_stack;
}
-static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop, BOOL recursive)
+static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop)
{
DEFINE_COMPILER;
-BOOL setsom_found = recursive;
-BOOL setmark_found = recursive;
+BOOL setsom_found = FALSE;
+BOOL setmark_found = FALSE;
/* The last capture is a local variable even for recursions. */
BOOL capture_last_found = FALSE;
int offset;
@@ -1671,7 +1710,7 @@ stackpos = STACK(stackpos);
if (ccend == NULL)
{
ccend = bracketend(cc) - (1 + LINK_SIZE);
- if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS))
+ if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)
cc = next_opcode(common, cc);
}
@@ -1685,9 +1724,9 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setsom_found = TRUE;
}
cc += 1;
@@ -1701,9 +1740,9 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setmark_found = TRUE;
}
cc += 1 + 2 + cc[1];
@@ -1714,27 +1753,27 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setsom_found = TRUE;
}
if (common->mark_ptr != 0 && !setmark_found)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setmark_found = TRUE;
}
if (common->capture_last_ptr != 0 && !capture_last_found)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
capture_last_found = TRUE;
}
cc += 1 + LINK_SIZE;
@@ -1748,20 +1787,20 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
capture_last_found = TRUE;
}
offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
cc += 1 + LINK_SIZE + IMM2_SIZE;
break;
@@ -1776,21 +1815,127 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0);
SLJIT_ASSERT(stackpos == STACK(stacktop));
}
-static SLJIT_INLINE int get_private_data_copy_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL needs_control_head)
+#define RECURSE_TMP_REG_COUNT 3
+
+typedef struct delayed_mem_copy_status {
+ struct sljit_compiler *compiler;
+ int store_bases[RECURSE_TMP_REG_COUNT];
+ int store_offsets[RECURSE_TMP_REG_COUNT];
+ int tmp_regs[RECURSE_TMP_REG_COUNT];
+ int saved_tmp_regs[RECURSE_TMP_REG_COUNT];
+ int next_tmp_reg;
+} delayed_mem_copy_status;
+
+static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common)
+{
+int i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ SLJIT_ASSERT(status->tmp_regs[i] >= 0);
+ SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]);
+
+ status->store_bases[i] = -1;
+ }
+status->next_tmp_reg = 0;
+status->compiler = common->compiler;
+}
+
+static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset,
+ int store_base, sljit_sw store_offset)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg = status->tmp_regs[next_tmp_reg];
+
+SLJIT_ASSERT(load_base > 0 && store_base > 0);
+
+if (status->store_bases[next_tmp_reg] == -1)
+ {
+ /* Preserve virtual registers. */
+ if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0)
+ OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0);
+ }
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset);
+status->store_bases[next_tmp_reg] = store_base;
+status->store_offsets[next_tmp_reg] = store_offset;
+
+status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+}
+
+static void delayed_mem_copy_finish(delayed_mem_copy_status *status)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg, saved_tmp_reg, i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ if (status->store_bases[next_tmp_reg] != -1)
+ {
+ tmp_reg = status->tmp_regs[next_tmp_reg];
+ saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg];
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+ /* Restore virtual registers. */
+ if (sljit_get_register_index(saved_tmp_reg) < 0)
+ OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0);
+ }
+
+ next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+ }
+}
+
+#undef RECURSE_TMP_REG_COUNT
+
+static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend,
+ BOOL *needs_control_head, BOOL *has_quit, BOOL *has_accept)
{
-int private_data_length = needs_control_head ? 3 : 2;
+int length = 1;
int size;
PCRE2_SPTR alternative;
+BOOL quit_found = FALSE;
+BOOL accept_found = FALSE;
+BOOL setsom_found = FALSE;
+BOOL setmark_found = FALSE;
+BOOL capture_last_found = FALSE;
+BOOL control_head_found = FALSE;
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+control_head_found = TRUE;
+#endif
+
/* Calculate the sum of the private machine words. */
while (cc < ccend)
{
size = 0;
switch(*cc)
{
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ setsom_found = TRUE;
+ cc += 1;
+ break;
+
+ case OP_RECURSE:
+ if (common->has_set_som)
+ setsom_found = TRUE;
+ if (common->mark_ptr != 0)
+ setmark_found = TRUE;
+ if (common->capture_last_ptr != 0)
+ capture_last_found = TRUE;
+ cc += 1 + LINK_SIZE;
+ break;
+
case OP_KET:
if (PRIVATE_DATA(cc) != 0)
{
- private_data_length++;
+ length++;
SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
cc += PRIVATE_DATA(cc + 1);
}
@@ -1802,26 +1947,30 @@ while (cc < ccend)
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRAPOS:
case OP_SBRA:
case OP_SBRAPOS:
case OP_SCOND:
- private_data_length++;
+ length++;
SLJIT_ASSERT(PRIVATE_DATA(cc) != 0);
cc += 1 + LINK_SIZE;
break;
case OP_CBRA:
case OP_SCBRA:
+ length += 2;
+ if (common->capture_last_ptr != 0)
+ capture_last_found = TRUE;
if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
- private_data_length++;
+ length++;
cc += 1 + LINK_SIZE + IMM2_SIZE;
break;
case OP_CBRAPOS:
case OP_SCBRAPOS:
- private_data_length += 2;
+ length += 2 + 2;
+ if (common->capture_last_ptr != 0)
+ capture_last_found = TRUE;
cc += 1 + LINK_SIZE + IMM2_SIZE;
break;
@@ -1829,13 +1978,13 @@ while (cc < ccend)
/* Might be a hidden SCOND. */
alternative = cc + GET(cc, 1);
if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
- private_data_length++;
+ length++;
cc += 1 + LINK_SIZE;
break;
CASE_ITERATOR_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
- private_data_length++;
+ if (PRIVATE_DATA(cc) != 0)
+ length++;
cc += 2;
#ifdef SUPPORT_UNICODE
if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
@@ -1843,8 +1992,8 @@ while (cc < ccend)
break;
CASE_ITERATOR_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- private_data_length += 2;
+ if (PRIVATE_DATA(cc) != 0)
+ length += 2;
cc += 2;
#ifdef SUPPORT_UNICODE
if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
@@ -1852,8 +2001,8 @@ while (cc < ccend)
break;
CASE_ITERATOR_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- private_data_length += 2;
+ if (PRIVATE_DATA(cc) != 0)
+ length += 2;
cc += 2 + IMM2_SIZE;
#ifdef SUPPORT_UNICODE
if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
@@ -1861,20 +2010,20 @@ while (cc < ccend)
break;
CASE_ITERATOR_TYPE_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
- private_data_length++;
+ if (PRIVATE_DATA(cc) != 0)
+ length++;
cc += 1;
break;
CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- private_data_length += 2;
+ if (PRIVATE_DATA(cc) != 0)
+ length += 2;
cc += 1;
break;
CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- private_data_length += 2;
+ if (PRIVATE_DATA(cc) != 0)
+ length += 2;
cc += 1 + IMM2_SIZE;
break;
@@ -1886,11 +2035,51 @@ while (cc < ccend)
#else
size = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
#endif
- if (PRIVATE_DATA(cc))
- private_data_length += get_class_iterator_size(cc + size);
+ if (PRIVATE_DATA(cc) != 0)
+ length += get_class_iterator_size(cc + size);
cc += size;
break;
+ case OP_MARK:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if (!setmark_found)
+ setmark_found = TRUE;
+ if (common->control_head_ptr != 0)
+ control_head_found = TRUE;
+ if (*cc != OP_MARK)
+ quit_found = TRUE;
+
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_SKIP:
+ case OP_COMMIT:
+ quit_found = TRUE;
+ cc++;
+ break;
+
+ case OP_SKIP_ARG:
+ quit_found = TRUE;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ quit_found = TRUE;
+ if (!control_head_found)
+ control_head_found = TRUE;
+ cc++;
+ break;
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ accept_found = TRUE;
+ cc++;
+ break;
+
default:
cc = next_opcode(common, cc);
SLJIT_ASSERT(cc != NULL);
@@ -1898,329 +2087,446 @@ while (cc < ccend)
}
}
SLJIT_ASSERT(cc == ccend);
-return private_data_length;
+
+if (control_head_found)
+ length++;
+if (capture_last_found)
+ length++;
+if (quit_found)
+ {
+ if (setsom_found)
+ length++;
+ if (setmark_found)
+ length++;
+ }
+
+*needs_control_head = control_head_found;
+*has_quit = quit_found;
+*has_accept = accept_found;
+return length;
}
-static void copy_private_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend,
- BOOL save, int stackptr, int stacktop, BOOL needs_control_head)
+enum copy_recurse_data_types {
+ recurse_copy_from_global,
+ recurse_copy_private_to_global,
+ recurse_copy_shared_to_global,
+ recurse_copy_kept_shared_to_global,
+ recurse_swap_global
+};
+
+static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend,
+ int type, int stackptr, int stacktop, BOOL has_quit)
{
-DEFINE_COMPILER;
-int srcw[2];
-int count, size;
-BOOL tmp1next = TRUE;
-BOOL tmp1empty = TRUE;
-BOOL tmp2empty = TRUE;
+delayed_mem_copy_status status;
PCRE2_SPTR alternative;
-enum {
- start,
- loop,
- end
-} status;
+sljit_sw private_srcw[2];
+sljit_sw shared_srcw[3];
+sljit_sw kept_shared_srcw[2];
+int private_count, shared_count, kept_shared_count;
+int from_sp, base_reg, offset, i;
+BOOL setsom_found = FALSE;
+BOOL setmark_found = FALSE;
+BOOL capture_last_found = FALSE;
+BOOL control_head_found = FALSE;
-status = save ? start : loop;
-stackptr = STACK(stackptr - 2);
-stacktop = STACK(stacktop - 1);
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+control_head_found = TRUE;
+#endif
-if (!save)
+switch (type)
{
- stackptr += (needs_control_head ? 2 : 1) * sizeof(sljit_sw);
- if (stackptr < stacktop)
- {
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr);
- stackptr += sizeof(sljit_sw);
- tmp1empty = FALSE;
- }
- if (stackptr < stacktop)
- {
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr);
- stackptr += sizeof(sljit_sw);
- tmp2empty = FALSE;
- }
- /* The tmp1next must be TRUE in either way. */
+ case recurse_copy_from_global:
+ from_sp = TRUE;
+ base_reg = STACK_TOP;
+ break;
+
+ case recurse_copy_private_to_global:
+ case recurse_copy_shared_to_global:
+ case recurse_copy_kept_shared_to_global:
+ from_sp = FALSE;
+ base_reg = STACK_TOP;
+ break;
+
+ default:
+ SLJIT_ASSERT(type == recurse_swap_global);
+ from_sp = FALSE;
+ base_reg = TMP2;
+ break;
}
-do
+stackptr = STACK(stackptr);
+stacktop = STACK(stacktop);
+
+status.tmp_regs[0] = TMP1;
+status.saved_tmp_regs[0] = TMP1;
+
+if (base_reg != TMP2)
{
- count = 0;
- switch(status)
+ status.tmp_regs[1] = TMP2;
+ status.saved_tmp_regs[1] = TMP2;
+ }
+else
+ {
+ status.saved_tmp_regs[1] = RETURN_ADDR;
+ if (sljit_get_register_index (RETURN_ADDR) == -1)
+ status.tmp_regs[1] = STR_PTR;
+ else
+ status.tmp_regs[1] = RETURN_ADDR;
+ }
+
+status.saved_tmp_regs[2] = TMP3;
+if (sljit_get_register_index (TMP3) == -1)
+ status.tmp_regs[2] = STR_END;
+else
+ status.tmp_regs[2] = TMP3;
+
+delayed_mem_copy_init(&status, common);
+
+if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+if (type != recurse_copy_shared_to_global)
+ {
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+#endif
+
+while (cc < ccend)
+ {
+ private_count = 0;
+ shared_count = 0;
+ kept_shared_count = 0;
+
+ switch(*cc)
{
- case start:
- SLJIT_ASSERT(save && common->recursive_head_ptr != 0);
- count = 1;
- srcw[0] = common->recursive_head_ptr;
- if (needs_control_head)
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ if (has_quit && !setsom_found)
{
- SLJIT_ASSERT(common->control_head_ptr != 0);
- count = 2;
- srcw[1] = common->control_head_ptr;
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ setsom_found = TRUE;
}
- status = loop;
+ cc += 1;
break;
- case loop:
- if (cc >= ccend)
+ case OP_RECURSE:
+ if (has_quit)
{
- status = end;
- break;
+ if (common->has_set_som && !setsom_found)
+ {
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ setsom_found = TRUE;
+ }
+ if (common->mark_ptr != 0 && !setmark_found)
+ {
+ kept_shared_srcw[kept_shared_count] = common->mark_ptr;
+ kept_shared_count++;
+ setmark_found = TRUE;
+ }
+ }
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ shared_srcw[0] = common->capture_last_ptr;
+ shared_count = 1;
+ capture_last_found = TRUE;
}
+ cc += 1 + LINK_SIZE;
+ break;
- switch(*cc)
+ case OP_KET:
+ if (PRIVATE_DATA(cc) != 0)
{
- case OP_KET:
- if (PRIVATE_DATA(cc) != 0)
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
- cc += PRIVATE_DATA(cc + 1);
- }
- cc += 1 + LINK_SIZE;
- break;
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- case OP_ONCE:
- case OP_ONCE_NC:
- case OP_BRAPOS:
- case OP_SBRA:
- case OP_SBRAPOS:
- case OP_SCOND:
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- SLJIT_ASSERT(srcw[0] != 0);
- cc += 1 + LINK_SIZE;
- break;
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ONCE:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ cc += 1 + LINK_SIZE;
+ break;
- case OP_CBRA:
- case OP_SCBRA:
- if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
- {
- count = 1;
- srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
- }
- cc += 1 + LINK_SIZE + IMM2_SIZE;
- break;
+ case OP_CBRA:
+ case OP_SCBRA:
+ offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
+ shared_srcw[0] = OVECTOR(offset);
+ shared_srcw[1] = OVECTOR(offset + 1);
+ shared_count = 2;
- case OP_CBRAPOS:
- case OP_SCBRAPOS:
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
- SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0);
- cc += 1 + LINK_SIZE + IMM2_SIZE;
- break;
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ shared_srcw[2] = common->capture_last_ptr;
+ shared_count = 3;
+ capture_last_found = TRUE;
+ }
- case OP_COND:
- /* Might be a hidden SCOND. */
- alternative = cc + GET(cc, 1);
- if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- SLJIT_ASSERT(srcw[0] != 0);
- }
- cc += 1 + LINK_SIZE;
- break;
+ if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
+ {
+ private_count = 1;
+ private_srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
+ }
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
- CASE_ITERATOR_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- }
- cc += 2;
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
+ shared_srcw[0] = OVECTOR(offset);
+ shared_srcw[1] = OVECTOR(offset + 1);
+ shared_count = 2;
+
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ shared_srcw[2] = common->capture_last_ptr;
+ shared_count = 3;
+ capture_last_found = TRUE;
+ }
+
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ }
+ cc += 2;
#ifdef SUPPORT_UNICODE
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
- }
- cc += 2;
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
+ }
+ cc += 2;
#ifdef SUPPORT_UNICODE
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
- }
- cc += 2 + IMM2_SIZE;
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
+ }
+ cc += 2 + IMM2_SIZE;
#ifdef SUPPORT_UNICODE
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- }
- cc += 1;
- break;
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ }
+ cc += 1;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = srcw[0] + sizeof(sljit_sw);
- }
- cc += 1;
- break;
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ }
+ cc += 1;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = srcw[0] + sizeof(sljit_sw);
- }
- cc += 1 + IMM2_SIZE;
- break;
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ if (PRIVATE_DATA(cc))
+ {
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
- case OP_CLASS:
- case OP_NCLASS:
+ case OP_CLASS:
+ case OP_NCLASS:
#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
- case OP_XCLASS:
- size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+ case OP_XCLASS:
+ i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
#else
- size = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+ i = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
#endif
- if (PRIVATE_DATA(cc))
- switch(get_class_iterator_size(cc + size))
- {
- case 1:
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- break;
-
- case 2:
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = srcw[0] + sizeof(sljit_sw);
- break;
+ if (PRIVATE_DATA(cc) != 0)
+ switch(get_class_iterator_size(cc + i))
+ {
+ case 1:
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ break;
+
+ case 2:
+ private_count = 2;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ cc += i;
+ break;
- default:
- SLJIT_ASSERT_STOP();
- break;
- }
- cc += size;
- break;
+ case OP_MARK:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if (has_quit && !setmark_found)
+ {
+ kept_shared_srcw[0] = common->mark_ptr;
+ kept_shared_count = 1;
+ setmark_found = TRUE;
+ }
+ if (common->control_head_ptr != 0 && !control_head_found)
+ {
+ shared_srcw[0] = common->control_head_ptr;
+ shared_count = 1;
+ control_head_found = TRUE;
+ }
+ cc += 1 + 2 + cc[1];
+ break;
- default:
- cc = next_opcode(common, cc);
- SLJIT_ASSERT(cc != NULL);
- break;
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ if (!control_head_found)
+ {
+ shared_srcw[0] = common->control_head_ptr;
+ shared_count = 1;
+ control_head_found = TRUE;
}
+ cc++;
break;
- case end:
- SLJIT_ASSERT_STOP();
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
break;
}
- while (count > 0)
+ if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
{
- count--;
- if (save)
- {
- if (tmp1next)
- {
- if (!tmp1empty)
- {
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0);
- stackptr += sizeof(sljit_sw);
- }
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]);
- tmp1empty = FALSE;
- tmp1next = FALSE;
- }
- else
- {
- if (!tmp2empty)
- {
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0);
- stackptr += sizeof(sljit_sw);
- }
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]);
- tmp2empty = FALSE;
- tmp1next = TRUE;
- }
- }
- else
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < private_count; i++)
{
- if (tmp1next)
- {
- SLJIT_ASSERT(!tmp1empty);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP1, 0);
- tmp1empty = stackptr >= stacktop;
- if (!tmp1empty)
- {
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr);
- stackptr += sizeof(sljit_sw);
- }
- tmp1next = FALSE;
- }
- else
- {
- SLJIT_ASSERT(!tmp2empty);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP2, 0);
- tmp2empty = stackptr >= stacktop;
- if (!tmp2empty)
- {
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr);
- stackptr += sizeof(sljit_sw);
- }
- tmp1next = TRUE;
- }
+ SLJIT_ASSERT(private_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
}
}
- }
-while (status != end);
+ else
+ stackptr += sizeof(sljit_sw) * private_count;
-if (save)
- {
- if (tmp1next)
+ if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global)
{
- if (!tmp1empty)
- {
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0);
- stackptr += sizeof(sljit_sw);
- }
- if (!tmp2empty)
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < shared_count; i++)
{
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0);
+ SLJIT_ASSERT(shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr);
+
stackptr += sizeof(sljit_sw);
}
}
else
+ stackptr += sizeof(sljit_sw) * shared_count;
+
+ if (type != recurse_copy_private_to_global && type != recurse_swap_global)
{
- if (!tmp2empty)
- {
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0);
- stackptr += sizeof(sljit_sw);
- }
- if (!tmp1empty)
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global);
+
+ for (i = 0; i < kept_shared_count; i++)
{
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0);
+ SLJIT_ASSERT(kept_shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr);
+
stackptr += sizeof(sljit_sw);
}
}
+ else
+ stackptr += sizeof(sljit_sw) * kept_shared_count;
}
-SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty)));
+
+SLJIT_ASSERT(cc == ccend && stackptr == stacktop);
+
+delayed_mem_copy_finish(&status);
}
static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset)
@@ -2337,7 +2643,7 @@ static SLJIT_INLINE void count_match(compiler_common *common)
{
DEFINE_COMPILER;
-OP2(SLJIT_SUB | SLJIT_SET_E, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
+OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO));
}
@@ -2347,7 +2653,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
DEFINE_COMPILER;
SLJIT_ASSERT(size > 0);
-OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
#ifdef DESTROY_REGISTERS
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
@@ -2355,7 +2661,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0);
#endif
-add_stub(common, CMP(SLJIT_GREATER, STACK_TOP, 0, STACK_LIMIT, 0));
+add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0));
}
static SLJIT_INLINE void free_stack(compiler_common *common, int size)
@@ -2363,7 +2669,7 @@ static SLJIT_INLINE void free_stack(compiler_common *common, int size)
DEFINE_COMPILER;
SLJIT_ASSERT(size > 0);
-OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
}
static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size)
@@ -2403,12 +2709,25 @@ if (length < 8)
}
else
{
- GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START);
- OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
- loop = LABEL();
- OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw), SLJIT_R0, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, loop);
+ if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START);
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0);
+ OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
}
}
@@ -2441,12 +2760,25 @@ if (length < 8)
}
else
{
- GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw));
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
- loop = LABEL();
- OP1(SLJIT_MOVU, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, loop);
+ if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
}
OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0);
@@ -2456,37 +2788,38 @@ if (common->control_head_ptr != 0)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack));
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
-OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, base));
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end));
}
-static sljit_sw SLJIT_CALL do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg)
+static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg)
{
while (current != NULL)
{
- switch (current[-2])
+ switch (current[1])
{
case type_then_trap:
break;
case type_mark:
- if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[-3]) == 0)
- return current[-4];
+ if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0)
+ return current[3];
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
- SLJIT_ASSERT(current > (sljit_sw*)current[-1]);
- current = (sljit_sw*)current[-1];
+ SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]);
+ current = (sljit_sw*)current[0];
}
-return -1;
+return 0;
}
static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket)
{
DEFINE_COMPILER;
struct sljit_label *loop;
+BOOL has_pre;
/* At this point we can freely use all registers. */
OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
@@ -2503,36 +2836,62 @@ if (common->mark_ptr != 0)
OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data),
SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE));
-GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START);
+has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS;
+
+GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0));
OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, begin));
loop = LABEL();
-OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0, SLJIT_R0, 0);
-OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
+
+if (has_pre)
+ sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw));
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0);
+ OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
+ }
+
+OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE));
+OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0);
/* Copy the integer value to the output buffer */
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
+
SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8);
-if (sizeof(PCRE2_SIZE) == 4)
- OP1(SLJIT_MOVU_U32, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0);
-else
- OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0);
+
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, loop);
/* Calculate the return value, which is the maximum ovector value. */
if (topbracket > 1)
{
- GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw));
- OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+ if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
- /* OVECTOR(0) is never equal to SLJIT_S2. */
- loop = LABEL();
- OP1(SLJIT_MOVU, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw)));
- OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
- CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
- OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw)));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0);
+ OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * (sljit_sw)sizeof(sljit_sw));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
}
else
OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1);
@@ -2543,7 +2902,7 @@ static SLJIT_INLINE void return_with_partial_match(compiler_common *common, stru
DEFINE_COMPILER;
sljit_s32 mov_opcode;
-SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S1, str_end_must_be_saved_reg2);
+SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0);
SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0
&& (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0));
@@ -2553,19 +2912,19 @@ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP),
OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL);
/* Store match begin and end. */
-OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin));
+OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0);
OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, match_data));
mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV;
-OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S0, 0);
+OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0);
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0);
-OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S0, 0);
+OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0);
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -3104,8 +3463,8 @@ if (common->utf)
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
/* Skip low surrogate if necessary. */
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
return;
@@ -3124,6 +3483,7 @@ struct sljit_jump *jump;
if (nltype == NLTYPE_ANY)
{
add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO));
}
else if (nltype == NLTYPE_ANYCRLF)
@@ -3165,7 +3525,7 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
/* Searching for the first zero. */
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
@@ -3179,7 +3539,7 @@ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000);
jump = JUMP(SLJIT_NOT_ZERO);
/* Three byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
@@ -3213,15 +3573,15 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
/* Searching for the first zero. */
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
JUMPHERE(jump);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_NOT_ZERO);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_NOT_ZERO);
/* This code runs only in 8 bit mode. No need to shift the value. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
@@ -3244,7 +3604,7 @@ struct sljit_jump *compare;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
@@ -3281,10 +3641,30 @@ static void do_getucd(compiler_common *common)
/* Search the UCD record for the character comes in TMP1.
Returns chartype in TMP1 and UCD offset in TMP2. */
DEFINE_COMPILER;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(INVALID_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Common && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8);
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
@@ -3299,7 +3679,7 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
#endif /* SUPPORT_UNICODE */
-static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, sljit_u32 overall_options)
+static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common)
{
DEFINE_COMPILER;
struct sljit_label *mainloop;
@@ -3311,6 +3691,8 @@ struct sljit_jump *end2 = NULL;
struct sljit_jump *singlechar;
#endif
jump_list *newline = NULL;
+sljit_u32 overall_options = common->re->overall_options;
+BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0;
BOOL newlinecheck = FALSE;
BOOL readuchar = FALSE;
@@ -3318,7 +3700,7 @@ if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0)
&& (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255))
newlinecheck = TRUE;
-SLJIT_ASSERT(common->forced_quit_label == NULL);
+SLJIT_ASSERT(common->abort_label == NULL);
if ((overall_options & PCRE2_FIRSTLINE) != 0)
{
@@ -3375,7 +3757,7 @@ else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0)
OP1(SLJIT_MOV, TMP2, 0, STR_END, 0);
JUMPHERE(end2);
OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
- add_jump(compiler, &common->forced_quit, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0));
+ add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0));
JUMPHERE(end);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0);
}
@@ -3388,8 +3770,8 @@ if (newlinecheck)
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -3426,8 +3808,8 @@ if (common->utf)
{
singlechar = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
JUMPHERE(singlechar);
@@ -3445,40 +3827,42 @@ if (newlinecheck)
return mainloop;
}
-#define MAX_N_CHARS 16
-#define MAX_DIFF_CHARS 6
-static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, PCRE2_UCHAR *chars)
+static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last)
{
-PCRE2_UCHAR i, len;
+sljit_u32 i, count = chars->count;
-len = chars[0];
-if (len == 255)
+if (count == 255)
return;
-if (len == 0)
+if (count == 0)
{
- chars[0] = 1;
- chars[1] = chr;
+ chars->count = 1;
+ chars->chars[0] = chr;
+
+ if (last)
+ chars->last_count = 1;
return;
}
-for (i = len; i > 0; i--)
- if (chars[i] == chr)
+for (i = 0; i < count; i++)
+ if (chars->chars[i] == chr)
return;
-if (len >= MAX_DIFF_CHARS - 1)
+if (count >= MAX_DIFF_CHARS)
{
- chars[0] = 255;
+ chars->count = 255;
return;
}
-len++;
-chars[len] = chr;
-chars[0] = len;
+chars->chars[count] = chr;
+chars->count = count + 1;
+
+if (last)
+ chars->last_count++;
}
-static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *chars, int max_chars, sljit_u32 *rec_count)
+static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count)
{
/* Recursive function, which scans prefix literals. */
BOOL last, any, class, caseless;
@@ -3487,7 +3871,7 @@ sljit_u32 chr; /* Any unicode character. */
sljit_u8 *bytes, *bytes_end, byte;
PCRE2_SPTR alternative, cc_save, oc;
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
-PCRE2_UCHAR othercase[8];
+PCRE2_UCHAR othercase[4];
#elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16
PCRE2_UCHAR othercase[2];
#else
@@ -3510,6 +3894,7 @@ while (TRUE)
{
case OP_CHARI:
caseless = TRUE;
+ /* Fall through */
case OP_CHAR:
last = FALSE;
cc++;
@@ -3541,6 +3926,7 @@ while (TRUE)
case OP_MINPLUSI:
case OP_POSPLUSI:
caseless = TRUE;
+ /* Fall through */
case OP_PLUS:
case OP_MINPLUS:
case OP_POSPLUS:
@@ -3549,6 +3935,7 @@ while (TRUE)
case OP_EXACTI:
caseless = TRUE;
+ /* Fall through */
case OP_EXACT:
repeat = GET2(cc, 1);
last = FALSE;
@@ -3559,6 +3946,7 @@ while (TRUE)
case OP_MINQUERYI:
case OP_POSQUERYI:
caseless = TRUE;
+ /* Fall through */
case OP_QUERY:
case OP_MINQUERY:
case OP_POSQUERY:
@@ -3582,7 +3970,6 @@ while (TRUE)
continue;
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_BRAPOS:
case OP_CBRA:
@@ -3703,12 +4090,12 @@ while (TRUE)
{
do
{
- chars[0] = 255;
+ chars->count = 255;
consumed++;
if (--max_chars == 0)
return consumed;
- chars += MAX_DIFF_CHARS;
+ chars++;
}
while (--repeat > 0);
@@ -3752,8 +4139,8 @@ while (TRUE)
do
{
if (bytes[31] & 0x80)
- chars[0] = 255;
- else if (chars[0] != 255)
+ chars->count = 255;
+ else if (chars->count != 255)
{
bytes_end = bytes + 32;
chr = 0;
@@ -3768,7 +4155,7 @@ while (TRUE)
do
{
if ((byte & 0x1) != 0)
- add_prefix_char(chr, chars);
+ add_prefix_char(chr, chars, TRUE);
byte >>= 1;
chr++;
}
@@ -3776,14 +4163,14 @@ while (TRUE)
chr = (chr + 7) & ~7;
}
}
- while (chars[0] != 255 && bytes < bytes_end);
+ while (chars->count != 255 && bytes < bytes_end);
bytes = bytes_end - 32;
}
consumed++;
if (--max_chars == 0)
return consumed;
- chars += MAX_DIFF_CHARS;
+ chars++;
}
while (--repeat > 0);
@@ -3847,17 +4234,18 @@ while (TRUE)
oc = othercase;
do
{
+ len--;
+ consumed++;
+
chr = *cc;
- add_prefix_char(*cc, chars);
+ add_prefix_char(*cc, chars, len == 0);
if (caseless)
- add_prefix_char(*oc, chars);
+ add_prefix_char(*oc, chars, len == 0);
- len--;
- consumed++;
if (--max_chars == 0)
return consumed;
- chars += MAX_DIFF_CHARS;
+ chars++;
cc++;
oc++;
}
@@ -3876,7 +4264,37 @@ while (TRUE)
}
}
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND)
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static struct sljit_jump *jump_if_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0x80);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
static sljit_s32 character_to_int32(PCRE2_UCHAR chr)
{
@@ -3895,39 +4313,140 @@ return value;
#endif
}
-static SLJIT_INLINE void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2)
+static void load_from_mem_sse2(struct sljit_compiler *compiler, sljit_s32 dst_xmm_reg, sljit_s32 src_general_reg)
+{
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+sljit_u8 instruction[5];
+#else
+sljit_u8 instruction[4];
+#endif
+
+SLJIT_ASSERT(dst_xmm_reg < 8);
+
+/* MOVDQA xmm1, xmm2/m128 */
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+if (src_general_reg < 8)
+ {
+ instruction[0] = 0x66;
+ instruction[1] = 0x0f;
+ instruction[2] = 0x6f;
+ instruction[3] = (dst_xmm_reg << 3) | src_general_reg;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+else
+ {
+ instruction[0] = 0x66;
+ instruction[1] = 0x41;
+ instruction[2] = 0x0f;
+ instruction[3] = 0x6f;
+ instruction[4] = (dst_xmm_reg << 3) | (src_general_reg & 0x7);
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+#else
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6f;
+instruction[3] = (dst_xmm_reg << 3) | src_general_reg;
+sljit_emit_op_custom(compiler, instruction, 4);
+#endif
+}
+
+static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, PCRE2_UCHAR char1, PCRE2_UCHAR char2,
+ sljit_u32 bit, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind)
+{
+sljit_u8 instruction[4];
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+
+if (char1 == char2 || bit != 0)
+ {
+ if (bit != 0)
+ {
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+else
+ {
+ /* MOVDQA xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x6f;
+ instruction[3] = 0xc0 | (tmp_ind << 3) | dst_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ instruction[3] = 0xc0 | (tmp_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+}
+
+static void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
{
DEFINE_COMPILER;
struct sljit_label *start;
-struct sljit_jump *quit[3];
-struct sljit_jump *nomatch;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *partial_quit[2];
sljit_u8 instruction[8];
sljit_s32 tmp1_ind = sljit_get_register_index(TMP1);
-sljit_s32 tmp2_ind = sljit_get_register_index(TMP2);
sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR);
-BOOL load_twice = FALSE;
-PCRE2_UCHAR bit;
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_u32 bit = 0;
-bit = char1 ^ char2;
-if (!is_powerof2(bit))
- bit = 0;
+SLJIT_UNUSED_ARG(offset);
-if ((char1 != char2) && bit == 0)
- load_twice = TRUE;
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ if (!is_powerof2(bit))
+ bit = 0;
+ }
-quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[0]);
/* First part (unaligned start) */
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
-SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1);
+SLJIT_ASSERT(tmp1_ind < 8);
/* MOVD xmm, r/m32 */
instruction[0] = 0x66;
instruction[1] = 0x0f;
instruction[2] = 0x6e;
-instruction[3] = 0xc0 | (2 << 3) | tmp1_ind;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 4);
if (char1 != char2)
@@ -3935,224 +4454,521 @@ if (char1 != char2)
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
/* MOVD xmm, r/m32 */
- instruction[3] = 0xc0 | (3 << 3) | tmp1_ind;
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 4);
}
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+
/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
instruction[2] = 0x70;
-instruction[3] = 0xc0 | (2 << 3) | 2;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | 2;
instruction[4] = 0;
sljit_emit_op_custom(compiler, instruction, 5);
if (char1 != char2)
{
/* PSHUFD xmm1, xmm2/m128, imm8 */
- instruction[3] = 0xc0 | (3 << 3) | 3;
- instruction[4] = 0;
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | 3;
sljit_emit_op_custom(compiler, instruction, 5);
}
-OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 0xf);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
-/* MOVDQA xmm1, xmm2/m128 */
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+load_from_mem_sse2(compiler, data_ind, str_ptr_ind);
+fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
-if (str_ptr_ind < 8)
- {
- instruction[2] = 0x6f;
- instruction[3] = (0 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 4);
-
- if (load_twice)
- {
- instruction[3] = (1 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 4);
- }
- }
-else
- {
- instruction[1] = 0x41;
- instruction[2] = 0x0f;
- instruction[3] = 0x6f;
- instruction[4] = (0 << 3) | (str_ptr_ind & 0x7);
- sljit_emit_op_custom(compiler, instruction, 5);
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
- if (load_twice)
- {
- instruction[4] = (1 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 5);
- }
- instruction[1] = 0x0f;
- }
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
-#else
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
-instruction[2] = 0x6f;
-instruction[3] = (0 << 3) | str_ptr_ind;
-sljit_emit_op_custom(compiler, instruction, 4);
+quit = JUMP(SLJIT_NOT_ZERO);
-if (load_twice)
- {
- instruction[3] = (1 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 4);
- }
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
-#endif
+start = LABEL();
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
-if (bit != 0)
- {
- /* POR xmm1, xmm2/m128 */
- instruction[2] = 0xeb;
- instruction[3] = 0xc0 | (0 << 3) | 3;
- sljit_emit_op_custom(compiler, instruction, 4);
- }
+partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[1]);
-/* PCMPEQB/W/D xmm1, xmm2/m128 */
-instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
-instruction[3] = 0xc0 | (0 << 3) | 2;
-sljit_emit_op_custom(compiler, instruction, 4);
+/* Second part (aligned) */
-if (load_twice)
- {
- instruction[3] = 0xc0 | (1 << 3) | 3;
- sljit_emit_op_custom(compiler, instruction, 4);
- }
+load_from_mem_sse2(compiler, 0, str_ptr_ind);
+fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
/* PMOVMSKB reg, xmm */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
instruction[2] = 0xd7;
instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
sljit_emit_op_custom(compiler, instruction, 4);
-if (load_twice)
- {
- OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0);
- instruction[3] = 0xc0 | (tmp2_ind << 3) | 1;
- sljit_emit_op_custom(compiler, instruction, 4);
-
- OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
- OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0);
- }
-
-OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0);
-
/* BSF r32, r/m32 */
instruction[0] = 0x0f;
instruction[1] = 0xbc;
instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
-nomatch = JUMP(SLJIT_ZERO);
+JUMPTO(SLJIT_ZERO, start);
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+JUMPHERE(quit);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
-quit[1] = JUMP(SLJIT_JUMP);
-JUMPHERE(nomatch);
+if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial_quit[0]);
+ JUMPHERE(partial_quit[1]);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+else
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
-start = LABEL();
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
-quit[2] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
-/* Second part (aligned) */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
-instruction[0] = 0x66;
-instruction[1] = 0x0f;
+ quit = jump_if_utf_char_start(compiler, TMP1);
-/* MOVDQA xmm1, xmm2/m128 */
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, restart);
-if (str_ptr_ind < 8)
+ JUMPHERE(quit);
+ }
+#endif
+}
+
+#ifndef _WIN64
+
+static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_sse2_offset(void)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return 15;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return 7;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+return 3;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+static void fast_forward_char_pair_sse2(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u32 bit1 = 0;
+sljit_u32 bit2 = 0;
+sljit_u32 diff = IN_UCHARS(offs1 - offs2);
+sljit_s32 tmp1_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data1_ind = 0;
+sljit_s32 data2_ind = 1;
+sljit_s32 tmp_ind = 2;
+sljit_s32 cmp1a_ind = 3;
+sljit_s32 cmp1b_ind = 4;
+sljit_s32 cmp2a_ind = 5;
+sljit_s32 cmp2b_ind = 6;
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *jump[2];
+
+sljit_u8 instruction[8];
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_sse2_offset()));
+SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1);
+
+/* Initialize. */
+if (common->match_end_ptr != 0)
{
- instruction[2] = 0x6f;
- instruction[3] = (0 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 4);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, STR_END, 0);
+ CMOV(SLJIT_LESS, STR_END, TMP1, 0);
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
- if (load_twice)
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+
+if (char1a == char1b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+else
+ {
+ bit1 = char1a ^ char1b;
+ if (is_powerof2(bit1))
{
- instruction[3] = (1 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 4);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a | bit1));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit1));
+ }
+ else
+ {
+ bit1 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char1b));
}
}
-else
+
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1a != char1b)
{
- instruction[1] = 0x41;
- instruction[2] = 0x0f;
- instruction[3] = 0x6f;
- instruction[4] = (0 << 3) | (str_ptr_ind & 0x7);
- sljit_emit_op_custom(compiler, instruction, 5);
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | tmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
- if (load_twice)
+if (char2a == char2b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+else
+ {
+ bit2 = char2a ^ char2b;
+ if (is_powerof2(bit2))
{
- instruction[4] = (1 << 3) | str_ptr_ind;
- sljit_emit_op_custom(compiler, instruction, 5);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a | bit2));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit2));
+ }
+ else
+ {
+ bit2 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char2b));
}
- instruction[1] = 0x0f;
}
-#else
-
-instruction[2] = 0x6f;
-instruction[3] = (0 << 3) | str_ptr_ind;
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 4);
-if (load_twice)
+if (char2a != char2b)
{
- instruction[3] = (1 << 3) | str_ptr_ind;
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | tmp2_ind;
sljit_emit_op_custom(compiler, instruction, 4);
}
-#endif
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[4] = 0;
-if (bit != 0)
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | cmp1a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1a != char1b)
{
- /* POR xmm1, xmm2/m128 */
- instruction[2] = 0xeb;
- instruction[3] = 0xc0 | (0 << 3) | 3;
- sljit_emit_op_custom(compiler, instruction, 4);
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | cmp1b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
}
-/* PCMPEQB/W/D xmm1, xmm2/m128 */
-instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
-instruction[3] = 0xc0 | (0 << 3) | 2;
-sljit_emit_op_custom(compiler, instruction, 4);
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | cmp2a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
-if (load_twice)
+if (char2a != char2b)
{
- instruction[3] = 0xc0 | (1 << 3) | 3;
- sljit_emit_op_custom(compiler, instruction, 4);
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | cmp2b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
}
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1 - offs2));
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, ~0xf);
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_ind);
+
+jump[0] = CMP(SLJIT_EQUAL, STR_PTR, 0, TMP1, 0);
+
+load_from_mem_sse2(compiler, data2_ind, tmp1_ind);
+
+/* MOVDQA xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x6f;
+instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PSLLDQ xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (7 << 3) | tmp_ind;
+instruction[4] = diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+/* PSRLDQ xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+/* instruction[2] = 0x73; */
+instruction[3] = 0xc0 | (3 << 3) | data2_ind;
+instruction[4] = 16 - diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+/* POR xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xeb;
+instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+jump[1] = JUMP(SLJIT_JUMP);
+
+JUMPHERE(jump[0]);
+
+/* MOVDQA xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x6f;
+instruction[3] = 0xc0 | (data2_ind << 3) | data1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PSLLDQ xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (7 << 3) | data2_ind;
+instruction[4] = diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+JUMPHERE(jump[1]);
+
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind);
+fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind);
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
instruction[2] = 0xd7;
instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
sljit_emit_op_custom(compiler, instruction, 4);
-if (load_twice)
- {
- instruction[3] = 0xc0 | (tmp2_ind << 3) | 1;
- sljit_emit_op_custom(compiler, instruction, 4);
+/* Ignore matches before the first STR_PTR. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
- OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
- }
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
+
+jump[0] = JUMP(SLJIT_NOT_ZERO);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Main loop. */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+
+start = LABEL();
+
+load_from_mem_sse2(compiler, data2_ind, str_ptr_ind);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_ind);
+
+/* PSRLDQ xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (3 << 3) | data2_ind;
+instruction[4] = 16 - diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+/* MOVDQA xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x6f;
+instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PSLLDQ xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (7 << 3) | tmp_ind;
+instruction[4] = diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+/* POR xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xeb;
+instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind);
+fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind);
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
/* BSF r32, r/m32 */
instruction[0] = 0x0f;
instruction[1] = 0xbc;
instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
JUMPTO(SLJIT_ZERO, start);
+JUMPHERE(jump[0]);
+
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
-start = LABEL();
-SET_LABEL(quit[0], start);
-SET_LABEL(quit[1], start);
-SET_LABEL(quit[2], start);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1));
+
+ jump[0] = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, restart);
+
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[0]);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+static BOOL check_fast_forward_char_pair_sse2(compiler_common *common, fast_forward_char_data *chars, int max)
+{
+sljit_s32 i, j, priority, count;
+sljit_u32 priorities;
+PCRE2_UCHAR a1, a2, b1, b2;
+
+priorities = 0;
+
+count = 0;
+for (i = 0; i < max; i++)
+ {
+ if (chars[i].last_count > 2)
+ {
+ SLJIT_ASSERT(chars[i].last_count <= 7);
+
+ priorities |= (1 << chars[i].last_count);
+ count++;
+ }
+ }
+
+if (count < 2)
+ return FALSE;
+
+for (priority = 7; priority > 2; priority--)
+ {
+ if ((priorities & (1 << priority)) == 0)
+ continue;
+
+ for (i = max - 1; i >= 1; i--)
+ if (chars[i].last_count >= priority)
+ {
+ SLJIT_ASSERT(chars[i].count <= 2 && chars[i].count >= 1);
+
+ a1 = chars[i].chars[0];
+ a2 = chars[i].chars[1];
+
+ j = i - max_fast_forward_char_pair_sse2_offset();
+ if (j < 0)
+ j = 0;
+
+ while (j < i)
+ {
+ if (chars[j].last_count >= priority)
+ {
+ b1 = chars[j].chars[0];
+ b2 = chars[j].chars[1];
+
+ if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2)
+ {
+ fast_forward_char_pair_sse2(common, i, a1, a2, j, b1, b2);
+ return TRUE;
+ }
+ }
+ j++;
+ }
+ }
+ }
+
+return FALSE;
}
+#endif
+
#undef SSE2_COMPARE_TYPE_INDEX
#endif
@@ -4161,15 +4977,16 @@ static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1,
{
DEFINE_COMPILER;
struct sljit_label *start;
-struct sljit_jump *quit;
-struct sljit_jump *found;
+struct sljit_jump *match;
+struct sljit_jump *partial_quit;
PCRE2_UCHAR mask;
-#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
-struct sljit_label *utf_start = NULL;
-struct sljit_jump *utf_quit = NULL;
-#endif
BOOL has_match_end = (common->match_end_ptr != 0);
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0);
+
+if (has_match_end)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+
if (offset > 0)
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
@@ -4177,76 +4994,21 @@ if (has_match_end)
{
OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
- OP2(SLJIT_ADD, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, SLJIT_IMM, IN_UCHARS(offset + 1));
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
- if (sljit_x86_is_cmov_available())
- {
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0);
- sljit_x86_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0);
- }
-#endif
- {
- quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP3, 0);
- OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
- JUMPHERE(quit);
- }
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
}
-#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
-if (common->utf && offset > 0)
- utf_start = LABEL();
-#endif
-
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND)
/* SSE2 accelerated first character search. */
-if (sljit_x86_is_sse2_available())
+if (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
{
- fast_forward_first_char2_sse2(common, char1, char2);
+ fast_forward_first_char2_sse2(common, char1, char2, offset);
- SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0);
- if (common->mode == PCRE2_JIT_COMPLETE)
- {
- /* In complete mode, we don't need to run a match when STR_PTR == STR_END. */
- SLJIT_ASSERT(common->forced_quit_label == NULL);
- OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
- add_jump(compiler, &common->forced_quit, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
-
-#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
- if (common->utf && offset > 0)
- {
- SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
-
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-#if PCRE2_CODE_UNIT_WIDTH == 8
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start);
-#elif PCRE2_CODE_UNIT_WIDTH == 16
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start);
-#else
-#error "Unknown code width"
-#endif
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- }
-#endif
-
- if (offset > 0)
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
- }
- else if (sljit_x86_is_cmov_available())
- {
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0);
- sljit_x86_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
- }
- else
- {
- quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_PTR, 0, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
- JUMPHERE(quit);
- }
+ if (offset > 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
if (has_match_end)
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
@@ -4255,85 +5017,56 @@ if (sljit_x86_is_sse2_available())
#endif
-quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
-
start = LABEL();
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
if (char1 == char2)
- found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start);
else
{
mask = char1 ^ char2;
if (is_powerof2(mask))
{
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
- found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start);
}
else
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- found = JUMP(SLJIT_NOT_ZERO);
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start);
+ JUMPHERE(match);
}
}
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, start);
-
-#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
-if (common->utf && offset > 0)
- utf_quit = JUMP(SLJIT_JUMP);
-#endif
-
-JUMPHERE(found);
-
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
if (common->utf && offset > 0)
{
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-#if PCRE2_CODE_UNIT_WIDTH == 8
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start);
-#elif PCRE2_CODE_UNIT_WIDTH == 16
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start);
-#else
-#error "Unknown code width"
-#endif
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- JUMPHERE(utf_quit);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1)));
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
}
#endif
-JUMPHERE(quit);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
if (has_match_end)
- {
- quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
- if (offset > 0)
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
- JUMPHERE(quit);
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
- }
-
-if (offset > 0)
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
}
static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common)
{
DEFINE_COMPILER;
struct sljit_label *start;
-struct sljit_jump *quit;
struct sljit_jump *match;
-/* bytes[0] represent the number of characters between 0
-and MAX_N_BYTES - 1, 255 represents any character. */
-PCRE2_UCHAR chars[MAX_N_CHARS * MAX_DIFF_CHARS];
+fast_forward_char_data chars[MAX_N_CHARS];
sljit_s32 offset;
PCRE2_UCHAR mask;
PCRE2_UCHAR *char_set, *char_set_end;
@@ -4344,7 +5077,10 @@ BOOL in_range;
sljit_u32 rec_count;
for (i = 0; i < MAX_N_CHARS; i++)
- chars[i * MAX_DIFF_CHARS] = 0;
+ {
+ chars[i].count = 0;
+ chars[i].last_count = 0;
+ }
rec_count = 10000;
max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count);
@@ -4352,21 +5088,50 @@ max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count);
if (max < 1)
return FALSE;
+/* Convert last_count to priority. */
+for (i = 0; i < max; i++)
+ {
+ SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count);
+
+ if (chars[i].count == 1)
+ {
+ chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5;
+ /* Simplifies algorithms later. */
+ chars[i].chars[1] = chars[i].chars[0];
+ }
+ else if (chars[i].count == 2)
+ {
+ SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]);
+
+ if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1]))
+ chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4;
+ else
+ chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2;
+ }
+ else
+ chars[i].last_count = (chars[i].count == 255) ? 0 : 1;
+ }
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) && !(defined _WIN64)
+if (check_fast_forward_char_pair_sse2(common, chars, max))
+ return TRUE;
+#endif
+
in_range = FALSE;
/* Prevent compiler "uninitialized" warning */
from = 0;
range_len = 4 /* minimum length */ - 1;
for (i = 0; i <= max; i++)
{
- if (in_range && (i - from) > range_len && (chars[(i - 1) * MAX_DIFF_CHARS] < 255))
+ if (in_range && (i - from) > range_len && (chars[i - 1].count < 255))
{
range_len = i - from;
range_right = i - 1;
}
- if (i < max && chars[i * MAX_DIFF_CHARS] < 255)
+ if (i < max && chars[i].count < 255)
{
- SLJIT_ASSERT(chars[i * MAX_DIFF_CHARS] > 0);
+ SLJIT_ASSERT(chars[i].count > 0);
if (!in_range)
{
in_range = TRUE;
@@ -4386,16 +5151,17 @@ if (range_right >= 0)
for (i = 0; i < range_len; i++)
{
- char_set = chars + ((range_right - i) * MAX_DIFF_CHARS);
- SLJIT_ASSERT(char_set[0] > 0 && char_set[0] < 255);
- char_set_end = char_set + char_set[0];
- char_set++;
- while (char_set <= char_set_end)
+ SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255);
+
+ char_set = chars[range_right - i].chars;
+ char_set_end = char_set + chars[range_right - i].count;
+ do
{
if (update_table[(*char_set) & 0xff] > IN_UCHARS(i))
update_table[(*char_set) & 0xff] = IN_UCHARS(i);
char_set++;
}
+ while (char_set < char_set_end);
}
}
@@ -4403,54 +5169,38 @@ offset = -1;
/* Scan forward. */
for (i = 0; i < max; i++)
{
+ if (range_right == i)
+ continue;
+
if (offset == -1)
{
- if (chars[i * MAX_DIFF_CHARS] <= 2)
+ if (chars[i].last_count >= 2)
offset = i;
}
- else if (chars[offset * MAX_DIFF_CHARS] == 2 && chars[i * MAX_DIFF_CHARS] <= 2)
- {
- if (chars[i * MAX_DIFF_CHARS] == 1)
- offset = i;
- else
- {
- mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2];
- if (!is_powerof2(mask))
- {
- mask = chars[i * MAX_DIFF_CHARS + 1] ^ chars[i * MAX_DIFF_CHARS + 2];
- if (is_powerof2(mask))
- offset = i;
- }
- }
- }
+ else if (chars[offset].last_count < chars[i].last_count)
+ offset = i;
}
+SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2));
+
if (range_right < 0)
{
if (offset < 0)
return FALSE;
- SLJIT_ASSERT(chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2);
/* Works regardless the value is 1 or 2. */
- mask = chars[offset * MAX_DIFF_CHARS + chars[offset * MAX_DIFF_CHARS]];
- fast_forward_first_char2(common, chars[offset * MAX_DIFF_CHARS + 1], mask, offset);
+ fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset);
return TRUE;
}
-if (range_right == offset)
- offset = -1;
+SLJIT_ASSERT(range_right != offset);
-SLJIT_ASSERT(offset == -1 || (chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2));
-
-max -= 1;
-SLJIT_ASSERT(max > 0);
if (common->match_end_ptr != 0)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
- quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP1, 0);
- OP1(SLJIT_MOV, STR_END, 0, TMP1, 0);
- JUMPHERE(quit);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
}
else
OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
@@ -4462,7 +5212,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table);
#endif
start = LABEL();
-quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
#if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right));
@@ -4483,20 +5233,20 @@ if (offset >= 0)
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset));
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- if (chars[offset * MAX_DIFF_CHARS] == 1)
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1], start);
+ if (chars[offset].count == 1)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start);
else
{
- mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2];
+ mask = chars[offset].chars[0] ^ chars[offset].chars[1];
if (is_powerof2(mask))
{
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1] | mask, start);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | mask, start);
}
else
{
- match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1]);
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 2], start);
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start);
JUMPHERE(match);
}
}
@@ -4512,15 +5262,9 @@ if (common->utf && offset != 0)
}
else
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
-#if PCRE2_CODE_UNIT_WIDTH == 8
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, start);
-#elif PCRE2_CODE_UNIT_WIDTH == 16
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, start);
-#else
-#error "Unknown code width"
-#endif
+
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
+
if (offset < 0)
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
@@ -4529,33 +5273,20 @@ if (common->utf && offset != 0)
if (offset >= 0)
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-JUMPHERE(quit);
-
if (common->match_end_ptr != 0)
- {
- if (range_right >= 0)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
- if (range_right >= 0)
- {
- quit = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP1, 0);
- OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
- JUMPHERE(quit);
- }
- }
else
OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
return TRUE;
}
-#undef MAX_N_CHARS
-
-static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, PCRE2_UCHAR first_char, BOOL caseless)
+static SLJIT_INLINE void fast_forward_first_char(compiler_common *common)
{
+PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit);
PCRE2_UCHAR oc;
oc = first_char;
-if (caseless)
+if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0)
{
oc = TABLE_GET(first_char, common->fcc, first_char);
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8
@@ -4593,8 +5324,8 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2));
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL);
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -4638,8 +5369,8 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
JUMPHERE(foundcr);
notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -4654,79 +5385,75 @@ if (common->match_end_ptr != 0)
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
}
-static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
-static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_u8 *start_bits)
+static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common)
{
DEFINE_COMPILER;
+const sljit_u8 *start_bits = common->re->start_bitmap;
struct sljit_label *start;
-struct sljit_jump *quit;
-struct sljit_jump *found = NULL;
-jump_list *matches = NULL;
+struct sljit_jump *partial_quit;
#if PCRE2_CODE_UNIT_WIDTH != 8
-struct sljit_jump *jump;
+struct sljit_jump *found = NULL;
#endif
+jump_list *matches = NULL;
if (common->match_end_ptr != 0)
{
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
}
start = LABEL();
-quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
-#ifdef SUPPORT_UNICODE
-if (common->utf)
- OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
-#endif
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-if (!check_class_ranges(common, start_bits, (start_bits[31] & 0x80) != 0, TRUE, &matches))
+if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches))
{
#if PCRE2_CODE_UNIT_WIDTH != 8
- jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 255);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 255);
- JUMPHERE(jump);
+ if ((start_bits[31] & 0x80) != 0)
+ found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255);
+ else
+ CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start);
+#elif defined SUPPORT_UNICODE
+ if (common->utf && is_char7_bitset(start_bits, FALSE))
+ CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start);
#endif
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
- OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
- found = JUMP(SLJIT_NOT_ZERO);
+ if (sljit_get_register_index(TMP3) >= 0)
+ {
+ OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP3, 0);
+ }
+ else
+ {
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ }
+ JUMPTO(SLJIT_ZERO, start);
}
+else
+ set_jumps(matches, start);
-#ifdef SUPPORT_UNICODE
-if (common->utf)
- OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
-#endif
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-#ifdef SUPPORT_UNICODE
-#if PCRE2_CODE_UNIT_WIDTH == 8
-if (common->utf)
- {
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start);
- OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
- }
-#elif PCRE2_CODE_UNIT_WIDTH == 16
-if (common->utf)
- {
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start);
- OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
- }
-#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */
-#endif /* SUPPORT_UNICODE */
-JUMPTO(SLJIT_JUMP, start);
+#if PCRE2_CODE_UNIT_WIDTH != 8
if (found != NULL)
JUMPHERE(found);
-if (matches != NULL)
- set_jumps(matches, LABEL());
-JUMPHERE(quit);
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
if (common->match_end_ptr != 0)
OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0);
@@ -4802,31 +5529,50 @@ struct sljit_jump *jump;
struct sljit_label *mainloop;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0);
-GET_LOCAL_BASE(TMP3, 0, 0);
+GET_LOCAL_BASE(TMP1, 0, 0);
/* Drop frames until we reach STACK_TOP. */
mainloop = LABEL();
-OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0);
-OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0);
-jump = JUMP(SLJIT_SIG_LESS_EQUAL);
-
-OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw));
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_sw));
-OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw));
+jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (sljit_get_register_index (TMP3) < 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw)));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw)));
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ GET_LOCAL_BASE(TMP1, 0, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0);
+ }
JUMPTO(SLJIT_JUMP, mainloop);
JUMPHERE(jump);
-jump = JUMP(SLJIT_SIG_LESS);
-/* End of dropping frames. */
+jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0);
+/* End of reverting values. */
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
JUMPHERE(jump);
OP1(SLJIT_NEG, TMP2, 0, TMP2, 0);
-OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw));
-OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_sw));
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (sljit_get_register_index (TMP3) < 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0);
+ }
JUMPTO(SLJIT_JUMP, mainloop);
}
@@ -4859,11 +5605,11 @@ if (common->use_ucp)
jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
JUMPHERE(jump);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0);
}
@@ -4903,11 +5649,11 @@ if (common->use_ucp)
jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
JUMPHERE(jump);
}
else
@@ -4935,15 +5681,15 @@ else
}
set_jumps(skipread_list, LABEL());
-OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP2(SLJIT_XOR | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
}
-static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
{
/* May destroy TMP1. */
DEFINE_COMPILER;
-int ranges[MAX_RANGE_SIZE];
+int ranges[MAX_CLASS_RANGE_SIZE];
sljit_u8 bit, cbit, all;
int i, byte, length = 0;
@@ -4961,7 +5707,7 @@ for (i = 0; i < 256; )
cbit = (bits[byte] >> (i & 0x7)) & 0x1;
if (cbit != bit)
{
- if (length >= MAX_RANGE_SIZE)
+ if (length >= MAX_CLASS_RANGE_SIZE)
return FALSE;
ranges[length] = i;
length++;
@@ -4974,7 +5720,7 @@ for (i = 0; i < 256; )
if (((bit == 0) && nclass) || ((bit == 1) && !nclass))
{
- if (length >= MAX_RANGE_SIZE)
+ if (length >= MAX_CLASS_RANGE_SIZE)
return FALSE;
ranges[length] = 256;
length++;
@@ -5086,9 +5832,116 @@ switch(length)
return TRUE;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
+ return FALSE;
+ }
+}
+
+static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+uint16_t char_list[MAX_CLASS_CHARS_SIZE];
+uint8_t byte;
+sljit_s32 type;
+int i, j, k, len, c;
+
+if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
return FALSE;
+
+if (invert)
+ nclass = !nclass;
+
+len = 0;
+
+for (i = 0; i < 32; i++)
+ {
+ byte = bits[i];
+
+ if (nclass)
+ byte = ~byte;
+
+ j = 0;
+ while (byte != 0)
+ {
+ if (byte & 0x1)
+ {
+ c = i * 8 + j;
+
+ k = len;
+
+ if ((c & 0x20) != 0)
+ {
+ for (k = 0; k < len; k++)
+ if (char_list[k] == c - 0x20)
+ {
+ char_list[k] |= 0x120;
+ break;
+ }
+ }
+
+ if (k == len)
+ {
+ if (len >= MAX_CLASS_CHARS_SIZE)
+ return FALSE;
+
+ char_list[len++] = (uint16_t) c;
+ }
+ }
+
+ byte >>= 1;
+ j++;
+ }
+ }
+
+i = 0;
+j = 0;
+
+if (char_list[0] == 0)
+ {
+ i++;
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO);
}
+else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+
+while (i < len)
+ {
+ if ((char_list[i] & 0x100) != 0)
+ j++;
+ else
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i]);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ i++;
+ }
+
+if (j != 0)
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20);
+
+ for (i = 0; i < len; i++)
+ if ((char_list[i] & 0x100) != 0)
+ {
+ j--;
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ }
+
+type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0));
+return TRUE;
+}
+
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+if (optimize_class_ranges(common, bits, nclass, invert, backtracks))
+ return TRUE;
+return optimize_class_chars(common, bits, nclass, invert, backtracks);
}
static void check_anynewline(compiler_common *common)
@@ -5099,22 +5952,22 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
-OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
#if PCRE2_CODE_UNIT_WIDTH == 8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
#if PCRE2_CODE_UNIT_WIDTH == 8
}
#endif
#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
@@ -5125,34 +5978,34 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20);
-OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20);
+OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0);
#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
#if PCRE2_CODE_UNIT_WIDTH == 8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
#if PCRE2_CODE_UNIT_WIDTH == 8
}
#endif
#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
@@ -5165,113 +6018,210 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
-OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
#if PCRE2_CODE_UNIT_WIDTH == 8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
#if PCRE2_CODE_UNIT_WIDTH == 8
}
#endif
#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
-#define CHAR1 STR_END
-#define CHAR2 STACK_TOP
-
static void do_casefulcmp(compiler_common *common)
{
DEFINE_COMPILER;
struct sljit_jump *jump;
struct sljit_label *label;
+int char1_reg;
+int char2_reg;
-sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+if (sljit_get_register_index(TMP3) < 0)
+ {
+ char1_reg = STR_END;
+ char2_reg = STACK_TOP;
+ }
+else
+ {
+ char1_reg = TMP3;
+ char2_reg = RETURN_ADDR;
+ }
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
-OP1(SLJIT_MOV, TMP3, 0, CHAR1, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR2, 0);
-OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
-OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-label = LABEL();
-OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1));
-OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
-jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
-JUMPTO(SLJIT_NOT_ZERO, label);
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0);
+ }
-JUMPHERE(jump);
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-OP1(SLJIT_MOV, CHAR1, 0, TMP3, 0);
-OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
-sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
-}
+if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ label = LABEL();
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
-#define LCC_TABLE STACK_LIMIT
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0);
+ }
+
+sljit_emit_fast_return(compiler, TMP1, 0);
+}
static void do_caselesscmp(compiler_common *common)
{
DEFINE_COMPILER;
struct sljit_jump *jump;
struct sljit_label *label;
+int char1_reg = STR_END;
+int char2_reg;
+int lcc_table;
+int opt_type = 0;
-sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+if (sljit_get_register_index(TMP3) < 0)
+ {
+ char2_reg = STACK_TOP;
+ lcc_table = STACK_LIMIT;
+ }
+else
+ {
+ char2_reg = RETURN_ADDR;
+ lcc_table = TMP3;
+ }
+
+if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 1;
+else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 2;
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
-OP1(SLJIT_MOV, TMP3, 0, LCC_TABLE, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR1, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, CHAR2, 0);
-OP1(SLJIT_MOV, LCC_TABLE, 0, SLJIT_IMM, common->lcc);
-OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
-OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0);
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0);
+ }
+
+OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc);
+
+if (opt_type == 1)
+ {
+ label = LABEL();
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else if (opt_type == 2)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
-label = LABEL();
-OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1));
-OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
#if PCRE2_CODE_UNIT_WIDTH != 8
-jump = CMP(SLJIT_GREATER, CHAR1, 0, SLJIT_IMM, 255);
+jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255);
#endif
-OP1(SLJIT_MOV_U8, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0);
+OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0);
#if PCRE2_CODE_UNIT_WIDTH != 8
JUMPHERE(jump);
-jump = CMP(SLJIT_GREATER, CHAR2, 0, SLJIT_IMM, 255);
+jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255);
#endif
-OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0);
+OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0);
#if PCRE2_CODE_UNIT_WIDTH != 8
JUMPHERE(jump);
#endif
-jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (opt_type == 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
JUMPTO(SLJIT_NOT_ZERO, label);
JUMPHERE(jump);
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-OP1(SLJIT_MOV, LCC_TABLE, 0, TMP3, 0);
-OP1(SLJIT_MOV, CHAR1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
-OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
-sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
-}
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
-#undef LCC_TABLE
-#undef CHAR1
-#undef CHAR2
+if (opt_type == 2)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0);
+ }
+
+OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+sljit_emit_fast_return(compiler, TMP1, 0);
+}
#if defined SUPPORT_UNICODE
-static PCRE2_SPTR SLJIT_CALL do_utf_caselesscmp(PCRE2_SPTR src1, jit_arguments *args, PCRE2_SPTR end1)
+static PCRE2_SPTR SLJIT_FUNC do_utf_caselesscmp(PCRE2_SPTR src1, PCRE2_SPTR src2, PCRE2_SPTR end1, PCRE2_SPTR end2)
{
/* This function would be ineffective to do in JIT level. */
sljit_u32 c1, c2;
-PCRE2_SPTR src2 = args->startchar_ptr;
-PCRE2_SPTR end2 = args->end;
const ucd_record *ur;
const sljit_u32 *pp;
@@ -5416,7 +6366,7 @@ do
#endif
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
context->ucharptr = 0;
@@ -5591,7 +6541,7 @@ while (*cc != XCL_END)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
cc += 2;
@@ -5609,13 +6559,13 @@ if ((cc[-1] & XCL_HASPROP) == 0)
if ((cc[-1] & XCL_MAP) != 0)
{
jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
- if (!check_class_ranges(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found))
+ if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found))
{
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO));
}
@@ -5636,7 +6586,7 @@ else if ((cc[-1] & XCL_MAP) != 0)
#ifdef SUPPORT_UNICODE
charsaved = TRUE;
#endif
- if (!check_class_ranges(common, (const sljit_u8 *)cc, FALSE, TRUE, list))
+ if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list))
{
#if PCRE2_CODE_UNIT_WIDTH == 8
jump = NULL;
@@ -5648,7 +6598,7 @@ else if ((cc[-1] & XCL_MAP) != 0)
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO));
#if PCRE2_CODE_UNIT_WIDTH == 8
@@ -5667,6 +6617,15 @@ if (needstype || needsscript)
if (needschar && !charsaved)
OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
@@ -5758,14 +6717,14 @@ while (*cc != XCL_END)
if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
numberofcmps++;
}
else if (numberofcmps > 0)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
numberofcmps = 0;
}
@@ -5784,14 +6743,14 @@ while (*cc != XCL_END)
if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
{
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
numberofcmps++;
}
else if (numberofcmps > 0)
{
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
numberofcmps = 0;
}
@@ -5816,12 +6775,12 @@ while (*cc != XCL_END)
break;
case PT_LAMP:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
@@ -5843,33 +6802,33 @@ while (*cc != XCL_END)
case PT_SPACE:
case PT_PXSPACE:
SET_CHAR_OFFSET(9);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
SET_TYPE_OFFSET(ucp_Zl);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_WORD:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
/* Fall through. */
case PT_ALNUM:
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, (*cc == PT_ALNUM) ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
SET_TYPE_OFFSET(ucp_Nd);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
@@ -5891,8 +6850,8 @@ while (*cc != XCL_END)
OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
}
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
other_cases += 2;
}
else if (is_powerof2(other_cases[2] ^ other_cases[1]))
@@ -5904,63 +6863,63 @@ while (*cc != XCL_END)
OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
}
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
- OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
+ OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
other_cases += 3;
}
else
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
}
while (*other_cases != NOTACHAR)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
- OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
}
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_UCNC:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
SET_CHAR_OFFSET(0xa0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
SET_CHAR_OFFSET(0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_GREATER_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_PXGRAPH:
/* C and Z groups are the farthest two groups. */
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
/* In case of ucp_Cf, we overwrite the result. */
SET_CHAR_OFFSET(0x2066);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
JUMPHERE(jump);
jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
@@ -5969,21 +6928,21 @@ while (*cc != XCL_END)
case PT_PXPRINT:
/* C and Z groups are the farthest two groups. */
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
- OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL);
jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
/* In case of ucp_Cf, we overwrite the result. */
SET_CHAR_OFFSET(0x2066);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
JUMPHERE(jump);
jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
@@ -5991,21 +6950,21 @@ while (*cc != XCL_END)
case PT_PXPUNCT:
SET_TYPE_OFFSET(ucp_Sc);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
SET_CHAR_OFFSET(0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f);
- OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL);
SET_TYPE_OFFSET(ucp_Pc);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
cc += 2;
@@ -6051,6 +7010,7 @@ switch(type)
case OP_NOT_WORD_BOUNDARY:
case OP_WORD_BOUNDARY:
add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6066,10 +7026,10 @@ switch(type)
else
{
jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL);
add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
check_partial(common, TRUE);
add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
@@ -6091,9 +7051,9 @@ switch(type)
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
jump[2] = JUMP(SLJIT_GREATER);
- add_jump(compiler, backtracks, JUMP(SLJIT_LESS));
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */);
/* Equal. */
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
@@ -6112,6 +7072,7 @@ switch(type)
read_char_range(common, common->nlmin, common->nlmax, TRUE);
add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
}
@@ -6129,8 +7090,8 @@ switch(type)
case OP_DOLL:
OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
- add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32));
if (!common->endonly)
compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks);
@@ -6144,8 +7105,8 @@ switch(type)
case OP_DOLLM:
jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
- add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32));
check_partial(common, FALSE);
jump[0] = JUMP(SLJIT_JUMP);
JUMPHERE(jump[1]);
@@ -6182,16 +7143,16 @@ switch(type)
OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
- OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
- add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32));
return cc;
case OP_CIRCM:
OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0);
- OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
- add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32));
jump[0] = JUMP(SLJIT_JUMP);
JUMPHERE(jump[1]);
@@ -6229,7 +7190,7 @@ switch(type)
label = LABEL();
add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0));
skip_char_back(common);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -6242,10 +7203,126 @@ switch(type)
check_start_used_ptr(common);
return cc + LINK_SIZE;
}
-SLJIT_ASSERT_STOP();
+SLJIT_UNREACHABLE();
+return cc;
+}
+
+#ifdef SUPPORT_UNICODE
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, len, ricount;
+PCRE2_SPTR prevcc, bptr;
+uint32_t c;
+
+prevcc = cc;
+GETCHARINC(c, cc);
+lgb = UCD_GRAPHBREAK(c);
+
+while (cc < end_subject)
+ {
+ len = 1;
+ GETCHARLEN(c, cc, len);
+ rgb = UCD_GRAPHBREAK(c);
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator)
+ {
+ ricount = 0;
+ bptr = prevcc;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend follows E_Base[_GAZ] do not update lgb; this allows
+ any number of Extend before a following E_Modifier. */
+
+ if (rgb != ucp_gbExtend || (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ))
+ lgb = rgb;
+
+ prevcc = cc;
+ cc += len;
+ }
+
return cc;
}
+#endif
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR bptr;
+uint32_t c;
+
+GETCHARINC(c, cc);
+lgb = UCD_GRAPHBREAK(c);
+
+while (cc < end_subject)
+ {
+ c = *cc;
+ rgb = UCD_GRAPHBREAK(c);
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator)
+ {
+ ricount = 0;
+ bptr = cc - 1;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ c = *bptr;
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend follows E_Base[_GAZ] do not update lgb; this allows
+ any number of Extend before a following E_Modifier. */
+
+ if (rgb != ucp_gbExtend || (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ))
+ lgb = rgb;
+
+ cc++;
+ }
+
+return cc;
+}
+
+#endif
+
static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr)
{
DEFINE_COMPILER;
@@ -6255,7 +7332,6 @@ compare_context context;
struct sljit_jump *jump[3];
jump_list *end_list;
#ifdef SUPPORT_UNICODE
-struct sljit_label *label;
PCRE2_UCHAR propdata[5];
#endif /* SUPPORT_UNICODE */
@@ -6273,7 +7349,7 @@ switch(type)
#endif
read_char8_type(common, type == OP_NOT_DIGIT);
/* Flip the starting bit in the negative case. */
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit);
add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6287,7 +7363,7 @@ switch(type)
else
#endif
read_char8_type(common, type == OP_NOT_WHITESPACE);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space);
add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6301,7 +7377,7 @@ switch(type)
else
#endif
read_char8_type(common, type == OP_NOT_WORDCHAR);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word);
add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6343,8 +7419,8 @@ switch(type)
#elif PCRE2_CODE_UNIT_WIDTH == 16
jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
#endif
@@ -6404,6 +7480,7 @@ switch(type)
detect_partial_match(common, backtracks);
read_char_range(common, 0x9, 0x3000, type == OP_NOT_HSPACE);
add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6413,6 +7490,7 @@ switch(type)
detect_partial_match(common, backtracks);
read_char_range(common, 0xa, 0x2029, type == OP_NOT_VSPACE);
add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6420,35 +7498,22 @@ switch(type)
case OP_EXTUNI:
if (check_str_ptr)
detect_partial_match(common, backtracks);
- read_char(common);
- add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop));
- /* Optimize register allocation: use a real register. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
- OP1(SLJIT_MOV_U8, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3);
- label = LABEL();
- jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
- OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
- read_char(common);
- add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop));
- OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3);
+ SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+ OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
- OP2(SLJIT_SHL, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2);
- OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable));
- OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0);
- OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
- JUMPTO(SLJIT_NOT_ZERO, label);
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM,
+ common->utf ? SLJIT_FUNC_OFFSET(do_extuni_utf) : SLJIT_FUNC_OFFSET(do_extuni_no_utf));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_extuni_no_utf));
+#endif
- OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
- JUMPHERE(jump[0]);
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
if (common->mode == PCRE2_JIT_PARTIAL_HARD)
{
- jump[0] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0);
/* Since we successfully read a char above, partial matching must occure. */
check_partial(common, TRUE);
JUMPHERE(jump[0]);
@@ -6582,7 +7647,7 @@ switch(type)
read_char_range(common, 0, 255, type == OP_NCLASS);
#endif
- if (check_class_ranges(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks))
+ if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks))
return cc + 32 / sizeof(PCRE2_UCHAR);
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
@@ -6609,7 +7674,7 @@ switch(type)
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
@@ -6626,7 +7691,7 @@ switch(type)
return cc + GET(cc, 0) - 1;
#endif
}
-SLJIT_ASSERT_STOP();
+SLJIT_UNREACHABLE();
return cc;
}
@@ -6781,40 +7846,42 @@ else
#if defined SUPPORT_UNICODE
if (common->utf && *cc == OP_REFI)
{
- SLJIT_ASSERT(TMP1 == SLJIT_R0 && STACK_TOP == SLJIT_R1 && TMP2 == SLJIT_R2);
+ SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
if (ref)
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
else
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
if (withchecks)
- jump = CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0);
-
- /* Needed to save important temporary registers. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
- OP1(SLJIT_MOV, SLJIT_R1, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), STR_PTR, 0);
- sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp));
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_R2, 0);
+ /* No free saved registers so save data on stack. */
+
+ OP1(SLJIT_MOV, SLJIT_R3, 0, STR_END, 0);
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW) | SLJIT_ARG4(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
if (common->mode == PCRE2_JIT_COMPLETE)
add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1));
else
{
- add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
- nopartial = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1);
+
+ add_jump(compiler, backtracks, JUMP(SLJIT_LESS));
+
+ nopartial = JUMP(SLJIT_NOT_EQUAL);
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
check_partial(common, FALSE);
add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
JUMPHERE(nopartial);
}
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
}
else
#endif /* SUPPORT_UNICODE */
{
if (ref)
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
else
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
if (withchecks)
jump = JUMP(SLJIT_ZERO);
@@ -6905,7 +7972,7 @@ switch(type)
cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE;
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -6919,7 +7986,7 @@ if (!minimize)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
/* Temporary release of STR_PTR. */
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
/* Handles both invalid and empty cases. Since the minimum repeat,
is zero the invalid case is basically the same as an empty case. */
if (ref)
@@ -6932,7 +7999,7 @@ if (!minimize)
zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
}
/* Restore if not zero length. */
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
}
else
{
@@ -7096,8 +8163,10 @@ if (entry == NULL)
if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
return NULL;
entry->next = NULL;
- entry->entry = NULL;
- entry->calls = NULL;
+ entry->entry_label = NULL;
+ entry->backtrack_label = NULL;
+ entry->entry_calls = NULL;
+ entry->backtrack_calls = NULL;
entry->start = start;
if (prev != NULL)
@@ -7106,71 +8175,74 @@ if (entry == NULL)
common->entries = entry;
}
-if (common->has_set_som && common->mark_ptr != 0)
- {
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
- allocate_stack(common, 2);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
- }
-else if (common->has_set_som || common->mark_ptr != 0)
- {
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr);
- allocate_stack(common, 1);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
- }
+BACKTRACK_AS(recurse_backtrack)->entry = entry;
-if (entry->entry == NULL)
- add_jump(compiler, &entry->calls, JUMP(SLJIT_FAST_CALL));
+if (entry->entry_label == NULL)
+ add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL));
else
- JUMPTO(SLJIT_FAST_CALL, entry->entry);
+ JUMPTO(SLJIT_FAST_CALL, entry->entry_label);
/* Leave if the match is failed. */
add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL();
return cc + 1 + LINK_SIZE;
}
-static int SLJIT_CALL do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector)
+static sljit_s32 SLJIT_FUNC do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector)
{
-PCRE2_SPTR begin = arguments->begin;
-PCRE2_SIZE *ovector = arguments->match_data->ovector;
-sljit_u32 oveccount = arguments->oveccount;
-sljit_u32 i;
+PCRE2_SPTR begin;
+PCRE2_SIZE *ovector;
+sljit_u32 oveccount, capture_top;
if (arguments->callout == NULL)
return 0;
-callout_block->version = 1;
+SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size);
+
+begin = arguments->begin;
+ovector = (PCRE2_SIZE*)(callout_block + 1);
+oveccount = callout_block->capture_top;
+
+SLJIT_ASSERT(oveccount >= 1);
+
+callout_block->version = 2;
+callout_block->callout_flags = 0;
/* Offsets in subject. */
callout_block->subject_length = arguments->end - arguments->begin;
-callout_block->start_match = (PCRE2_SPTR)callout_block->subject - arguments->begin;
-callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - arguments->begin;
+callout_block->start_match = jit_ovector[0] - begin;
+callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin;
callout_block->subject = begin;
/* Convert and copy the JIT offset vector to the ovector array. */
-callout_block->capture_top = 0;
+callout_block->capture_top = 1;
callout_block->offset_vector = ovector;
-for (i = 2; i < oveccount; i += 2)
- {
- ovector[i] = jit_ovector[i] - begin;
- ovector[i + 1] = jit_ovector[i + 1] - begin;
- if (jit_ovector[i] >= begin)
- callout_block->capture_top = i;
- }
-callout_block->capture_top = (callout_block->capture_top >> 1) + 1;
ovector[0] = PCRE2_UNSET;
ovector[1] = PCRE2_UNSET;
+ovector += 2;
+jit_ovector += 2;
+capture_top = 1;
+
+/* Convert pointers to sizes. */
+while (--oveccount != 0)
+ {
+ capture_top++;
+
+ ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin);
+ ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin);
+
+ if (ovector[0] != PCRE2_UNSET)
+ callout_block->capture_top = capture_top;
+
+ ovector += 2;
+ jit_ovector += 2;
+ }
+
return (arguments->callout)(callout_block, arguments->callout_data);
}
-/* Aligning to 8 byte. */
-#define CALLOUT_ARG_SIZE \
- (((int)sizeof(pcre2_callout_block) + 7) & ~7)
-
#define CALLOUT_ARG_OFFSET(arg) \
- (-CALLOUT_ARG_SIZE + SLJIT_OFFSETOF(pcre2_callout_block, arg))
+ SLJIT_OFFSETOF(pcre2_callout_block, arg)
static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
{
@@ -7182,10 +8254,13 @@ unsigned int callout_length = (*cc == OP_CALLOUT)
sljit_sw value1;
sljit_sw value2;
sljit_sw value3;
+sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * sizeof(sljit_sw);
PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
-allocate_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw));
+callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw);
+
+allocate_stack(common, callout_arg_size);
SLJIT_ASSERT(common->capture_last_ptr != 0);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
@@ -7193,11 +8268,10 @@ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0;
OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1);
OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0);
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1);
/* These pointer sized fields temporarly stores internal variables. */
-OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(subject), TMP2, 0);
if (common->mark_ptr != 0)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr));
@@ -7223,22 +8297,24 @@ OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length)
OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0);
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
/* Needed to save important temporary registers. */
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
-OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+/* SLJIT_R0 = arguments */
+OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0);
GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START);
-sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout));
-OP1(SLJIT_MOV_S32, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0);
-OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
-free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw));
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(S32) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout));
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+free_stack(common, callout_arg_size);
/* Check return value. */
-OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
-add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER));
-if (common->forced_quit_label == NULL)
- add_jump(compiler, &common->forced_quit, JUMP(SLJIT_SIG_LESS));
+OP2(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER32));
+if (common->abort_label == NULL)
+ add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL32) /* SIG_LESS */);
else
- JUMPTO(SLJIT_SIG_LESS, common->forced_quit_label);
+ JUMPTO(SLJIT_NOT_EQUAL32 /* SIG_LESS */, common->abort_label);
return cc + callout_length;
}
@@ -7280,6 +8356,7 @@ static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPT
DEFINE_COMPILER;
int framesize;
int extrasize;
+BOOL local_quit_available = FALSE;
BOOL needs_control_head;
int private_data_ptr;
backtrack_common altbacktrack;
@@ -7290,13 +8367,13 @@ jump_list *tmp = NULL;
jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks;
jump_list **found;
/* Saving previous accept variables. */
-BOOL save_local_exit = common->local_exit;
-BOOL save_positive_assert = common->positive_assert;
+BOOL save_local_quit_available = common->local_quit_available;
+BOOL save_in_positive_assertion = common->in_positive_assertion;
then_trap_backtrack *save_then_trap = common->then_trap;
struct sljit_label *save_quit_label = common->quit_label;
struct sljit_label *save_accept_label = common->accept_label;
jump_list *save_quit = common->quit;
-jump_list *save_positive_assert_quit = common->positive_assert_quit;
+jump_list *save_positive_assertion_quit = common->positive_assertion_quit;
jump_list *save_accept = common->accept;
struct sljit_jump *jump;
struct sljit_jump *brajump = NULL;
@@ -7363,7 +8440,7 @@ else
allocate_stack(common, framesize + extrasize);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
if (needs_control_head)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
@@ -7378,21 +8455,21 @@ else
else
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
- init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE);
+ init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize);
}
memset(&altbacktrack, 0, sizeof(backtrack_common));
-if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT))
{
- /* Negative assert is stronger than positive assert. */
- common->local_exit = TRUE;
+ /* Control verbs cannot escape from these asserts. */
+ local_quit_available = TRUE;
+ common->local_quit_available = TRUE;
common->quit_label = NULL;
common->quit = NULL;
- common->positive_assert = FALSE;
}
-else
- common->positive_assert = TRUE;
-common->positive_assert_quit = NULL;
+
+common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK);
+common->positive_assertion_quit = NULL;
while (1)
{
@@ -7408,16 +8485,16 @@ while (1)
compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack);
if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
{
- if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+ if (local_quit_available)
{
- common->local_exit = save_local_exit;
+ common->local_quit_available = save_local_quit_available;
common->quit_label = save_quit_label;
common->quit = save_quit;
}
- common->positive_assert = save_positive_assert;
+ common->in_positive_assertion = save_in_positive_assertion;
common->then_trap = save_then_trap;
common->accept_label = save_accept_label;
- common->positive_assert_quit = save_positive_assert_quit;
+ common->positive_assertion_quit = save_positive_assertion_quit;
common->accept = save_accept;
return NULL;
}
@@ -7434,23 +8511,24 @@ while (1)
free_stack(common, extrasize);
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
else
{
if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional)
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
else
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2));
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
}
}
@@ -7460,25 +8538,25 @@ while (1)
if (conditional)
{
if (extrasize > 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1));
}
else if (bra == OP_BRAZERO)
{
if (framesize < 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
else
{
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
}
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
else if (framesize >= 0)
{
/* For OP_BRA and OP_BRAMINZERO. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
}
}
add_jump(compiler, found, JUMP(SLJIT_JUMP));
@@ -7486,16 +8564,16 @@ while (1)
compile_backtrackingpath(common, altbacktrack.top);
if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
{
- if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+ if (local_quit_available)
{
- common->local_exit = save_local_exit;
+ common->local_quit_available = save_local_quit_available;
common->quit_label = save_quit_label;
common->quit = save_quit;
}
- common->positive_assert = save_positive_assert;
+ common->in_positive_assertion = save_in_positive_assertion;
common->then_trap = save_then_trap;
common->accept_label = save_accept_label;
- common->positive_assert_quit = save_positive_assert_quit;
+ common->positive_assertion_quit = save_positive_assertion_quit;
common->accept = save_accept;
return NULL;
}
@@ -7508,26 +8586,26 @@ while (1)
cc += GET(cc, 1);
}
-if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+if (local_quit_available)
{
- SLJIT_ASSERT(common->positive_assert_quit == NULL);
+ SLJIT_ASSERT(common->positive_assertion_quit == NULL);
/* Makes the check less complicated below. */
- common->positive_assert_quit = common->quit;
+ common->positive_assertion_quit = common->quit;
}
/* None of them matched. */
-if (common->positive_assert_quit != NULL)
+if (common->positive_assertion_quit != NULL)
{
jump = JUMP(SLJIT_JUMP);
- set_jumps(common->positive_assert_quit, LABEL());
+ set_jumps(common->positive_assertion_quit, LABEL());
SLJIT_ASSERT(framesize != no_stack);
if (framesize < 0)
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
else
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw));
}
JUMPHERE(jump);
}
@@ -7576,18 +8654,18 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
{
/* We know that STR_PTR was stored on the top of the stack. */
if (extrasize > 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
/* Keep the STR_PTR on the top of the stack. */
if (bra == OP_BRAZERO)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
if (extrasize == 2)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
}
else if (bra == OP_BRAMINZERO)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
}
@@ -7596,13 +8674,13 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
if (bra == OP_BRA)
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 2) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1));
}
else
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
if (extrasize == 2)
{
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
@@ -7630,7 +8708,9 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
}
set_jumps(backtrack->common.topbacktracks, LABEL());
}
@@ -7683,16 +8763,16 @@ else
}
}
-if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+if (local_quit_available)
{
- common->local_exit = save_local_exit;
+ common->local_quit_available = save_local_quit_available;
common->quit_label = save_quit_label;
common->quit = save_quit;
}
-common->positive_assert = save_positive_assert;
+common->in_positive_assertion = save_in_positive_assertion;
common->then_trap = save_then_trap;
common->accept_label = save_accept_label;
-common->positive_assert_quit = save_positive_assert_quit;
+common->positive_assertion_quit = save_positive_assertion_quit;
common->accept = save_accept;
return cc + 1 + LINK_SIZE;
}
@@ -7717,23 +8797,23 @@ if (framesize < 0)
}
if (needs_control_head)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? sizeof(sljit_sw) : 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1));
/* TMP2 which is set here used by OP_KETRMAX below. */
if (ket == OP_KETRMAX)
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
else if (ket == OP_KETRMIN)
{
/* Move the STR_PTR to the private_data_ptr. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
}
else
{
stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1;
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
if (needs_control_head)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
if (ket == OP_KETRMAX)
{
@@ -7820,7 +8900,6 @@ return stacksize;
(|) OP_*BRA | OP_ALT ... M A
(?()|) OP_*COND | OP_ALT M A
(?>|) OP_ONCE | OP_ALT ... [stack trace] M A
- (?>|) OP_ONCE_NC | OP_ALT ... [stack trace] M A
Or nothing, if trace is unnecessary
*/
@@ -7888,8 +8967,6 @@ if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND))
if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
opcode = OP_SCOND;
-if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC))
- opcode = OP_ONCE;
if (opcode == OP_CBRA || opcode == OP_SCBRA)
{
@@ -7966,7 +9043,7 @@ if (bra == OP_BRAMINZERO)
{
/* Except when the whole stack frame must be saved. */
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (BACKTRACK_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw));
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2));
}
JUMPHERE(skip);
}
@@ -8039,7 +9116,7 @@ if (opcode == OP_ONCE)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
}
else if (ket == OP_KETRMAX || has_alternatives)
@@ -8057,7 +9134,7 @@ if (opcode == OP_ONCE)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
stacksize = needs_control_head ? 1 : 0;
if (ket != OP_KET || has_alternatives)
@@ -8072,7 +9149,7 @@ if (opcode == OP_ONCE)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
}
- init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE);
+ init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1);
}
}
else if (opcode == OP_CBRA || opcode == OP_SCBRA)
@@ -8129,13 +9206,13 @@ if (opcode == OP_COND || opcode == OP_SCOND)
slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
slot += common->name_entry_size;
i--;
while (i-- > 0)
{
OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
- OP2(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0);
slot += common->name_entry_size;
}
OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
@@ -8288,7 +9365,7 @@ if (ket == OP_KETRMAX)
{
if (has_alternatives)
BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, rmax_label);
/* Drop STR_PTR for greedy plus quantifier. */
if (opcode != OP_ONCE)
@@ -8318,7 +9395,7 @@ if (ket == OP_KETRMAX)
if (repeat_type == OP_EXACT)
{
count_match(common);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, rmax_label);
}
else if (repeat_type == OP_UPTO)
@@ -8346,6 +9423,7 @@ if (bra == OP_BRAMINZERO)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
}
else if (ket == OP_KETRMIN && opcode != OP_ONCE)
free_stack(common, 1);
@@ -8418,7 +9496,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -8496,7 +9574,7 @@ else
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
if (needs_control_head)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
stack = 0;
if (!zero)
@@ -8515,7 +9593,7 @@ else
stack++;
}
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0);
- init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize, FALSE);
+ init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize);
stack -= 1 + (offset == 0);
}
@@ -8568,7 +9646,7 @@ while (*cc != OP_KETRPOS)
{
if (offset != 0)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
@@ -8579,10 +9657,10 @@ while (*cc != OP_KETRPOS)
else
{
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
if (opcode == OP_SBRAPOS)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw));
- OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0);
}
/* Even if the match is empty, we need to reset the control head. */
@@ -8628,7 +9706,7 @@ while (*cc != OP_KETRPOS)
else
{
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
}
}
@@ -8645,7 +9723,7 @@ if (!zero)
if (framesize < 0)
add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0));
else /* TMP2 is set to [private_data_ptr] above. */
- add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_sw), SLJIT_IMM, 0));
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0));
}
/* None of them matched. */
@@ -8868,7 +9946,7 @@ if (exact > 1)
OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
label = LABEL();
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -8876,7 +9954,7 @@ if (exact > 1)
OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
label = LABEL();
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
}
@@ -8906,7 +9984,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
jump = JUMP(SLJIT_ZERO);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
}
@@ -8968,7 +10046,7 @@ switch(opcode)
label = LABEL();
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO));
}
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
@@ -8988,7 +10066,7 @@ switch(opcode)
OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
}
@@ -9015,7 +10093,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -9044,7 +10122,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -9070,7 +10148,7 @@ switch(opcode)
compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
@@ -9157,7 +10235,7 @@ switch(opcode)
label = LABEL();
compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
set_jumps(no_match, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
@@ -9168,7 +10246,7 @@ switch(opcode)
label = LABEL();
detect_partial_match(common, &no_match);
compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
set_jumps(no_char1_match, LABEL());
@@ -9186,7 +10264,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -9207,6 +10285,9 @@ if (*cc == OP_FAIL)
return cc + 1;
}
+if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0)
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+
if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty)
{
/* No need to check notempty conditions. */
@@ -9223,9 +10304,9 @@ else
CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO));
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
if (common->accept_label == NULL)
add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO));
else
@@ -9309,7 +10390,7 @@ size = 3 + (size < 0 ? 0 : size);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
allocate_stack(common, size);
if (size > 3)
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
else
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start);
@@ -9318,7 +10399,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0);
size = BACKTRACK_AS(then_trap_backtrack)->framesize;
if (size >= 0)
- init_frame(common, cc, ccend, size - 1, 0, FALSE);
+ init_frame(common, cc, ccend, size - 1, 0);
}
static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent)
@@ -9540,7 +10621,6 @@ while (cc < ccend)
break;
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_CBRA:
case OP_COND:
@@ -9615,7 +10695,7 @@ while (cc < ccend)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return;
}
if (cc == NULL)
@@ -9723,7 +10803,7 @@ switch(opcode)
case OP_MINUPTO:
OP1(SLJIT_MOV, TMP1, 0, base, offset1);
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO));
OP1(SLJIT_MOV, base, offset1, TMP1, 0);
@@ -9769,7 +10849,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -9804,27 +10884,21 @@ free_stack(common, ref ? 2 : 3);
static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current)
{
DEFINE_COMPILER;
+recurse_entry *entry;
-if (CURRENT_AS(recurse_backtrack)->inlined_pattern)
- compile_backtrackingpath(common, current->top);
-set_jumps(current->topbacktracks, LABEL());
-if (CURRENT_AS(recurse_backtrack)->inlined_pattern)
- return;
-
-if (common->has_set_som && common->mark_ptr != 0)
+if (!CURRENT_AS(recurse_backtrack)->inlined_pattern)
{
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
- free_stack(common, 2);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP2, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0);
- }
-else if (common->has_set_som || common->mark_ptr != 0)
- {
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
- free_stack(common, 1);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr, TMP2, 0);
+ entry = CURRENT_AS(recurse_backtrack)->entry;
+ if (entry->backtrack_label == NULL)
+ add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL));
+ else
+ JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath);
}
+else
+ compile_backtrackingpath(common, current->top);
+
+set_jumps(current->topbacktracks, LABEL());
}
static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current)
@@ -9877,7 +10951,9 @@ if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_backtrack)->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0);
set_jumps(current->topbacktracks, LABEL());
}
@@ -9887,7 +10963,7 @@ else
if (bra == OP_BRAZERO)
{
/* We know there is enough place on the stack. */
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath);
JUMPHERE(brajump);
@@ -9947,8 +11023,6 @@ if (opcode == OP_CBRA || opcode == OP_SCBRA)
offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1;
if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
opcode = OP_SCOND;
-if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC))
- opcode = OP_ONCE;
alt_max = has_alternatives ? no_alternatives(ccbegin) : 0;
@@ -10000,7 +11074,7 @@ else if (ket == OP_KETRMIN)
else
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
}
/* Drop STR_PTR for non-greedy plus quantifier. */
if (opcode != OP_ONCE)
@@ -10054,6 +11128,7 @@ if (SLJIT_UNLIKELY(opcode == OP_ONCE))
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
}
once = JUMP(SLJIT_JUMP);
}
@@ -10106,7 +11181,9 @@ if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
}
cond = JUMP(SLJIT_JUMP);
set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL());
@@ -10247,7 +11324,9 @@ if (has_alternatives)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
}
JUMPHERE(cond);
}
@@ -10302,7 +11381,7 @@ else if (opcode == OP_ONCE)
JUMPHERE(once);
/* Restore previous private_data_ptr */
if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_backtrack)->u.framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1));
else if (ket == OP_KETRMIN)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
@@ -10383,6 +11462,7 @@ if (CURRENT_AS(bracketpos_backtrack)->framesize < 0)
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw));
if (current->topbacktracks)
{
@@ -10392,7 +11472,7 @@ if (current->topbacktracks)
free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
JUMPHERE(jump);
}
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw));
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1));
}
static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
@@ -10438,22 +11518,23 @@ if (opcode == OP_THEN || opcode == OP_THEN_ARG)
jump = JUMP(SLJIT_JUMP);
loop = LABEL();
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), -(int)sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
JUMPHERE(jump);
- CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(2 * sizeof(sljit_sw)), TMP1, 0, loop);
- CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(3 * sizeof(sljit_sw)), TMP2, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop);
add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP));
return;
}
- else if (common->positive_assert)
+ else if (!common->local_quit_available && common->in_positive_assertion)
{
- add_jump(compiler, &common->positive_assert_quit, JUMP(SLJIT_JUMP));
+ add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP));
return;
}
}
-if (common->local_exit)
+if (common->local_quit_available)
{
+ /* Abort match with a fail. */
if (common->quit_label == NULL)
add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
else
@@ -10463,15 +11544,13 @@ if (common->local_exit)
if (opcode == OP_SKIP_ARG)
{
- SLJIT_ASSERT(common->control_head_ptr != 0);
+ SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2));
- sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark));
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2));
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark));
- OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
- add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0);
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0));
return;
}
@@ -10504,7 +11583,10 @@ jump = JUMP(SLJIT_JUMP);
set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL());
/* STACK_TOP is set by THEN. */
if (CURRENT_AS(then_trap_backtrack)->framesize >= 0)
+ {
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ }
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
free_stack(common, 3);
@@ -10621,7 +11703,6 @@ while (current)
break;
case OP_ONCE:
- case OP_ONCE_NC:
case OP_BRA:
case OP_CBRA:
case OP_COND:
@@ -10670,7 +11751,7 @@ while (current)
break;
case OP_COMMIT:
- if (!common->local_exit)
+ if (!common->local_quit_available)
OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
if (common->quit_label == NULL)
add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
@@ -10692,7 +11773,7 @@ while (current)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
current = current->prev;
@@ -10707,38 +11788,52 @@ PCRE2_SPTR cc = common->start + common->currententry->start;
PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE);
PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE);
BOOL needs_control_head;
-int framesize = get_framesize(common, cc, NULL, TRUE, &needs_control_head);
-int private_data_size = get_private_data_copy_length(common, ccbegin, ccend, needs_control_head);
-int alternativesize;
-BOOL needs_frame;
+BOOL has_quit;
+BOOL has_accept;
+int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &needs_control_head, &has_quit, &has_accept);
+int alt_count, alt_max, local_size;
backtrack_common altbacktrack;
-struct sljit_jump *jump;
+jump_list *match = NULL;
+sljit_uw *next_update_addr = NULL;
+struct sljit_jump *alt1 = NULL;
+struct sljit_jump *alt2 = NULL;
+struct sljit_jump *accept_exit = NULL;
+struct sljit_label *quit;
/* Recurse captures then. */
common->then_trap = NULL;
SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS);
-needs_frame = framesize >= 0;
-if (!needs_frame)
- framesize = 0;
-alternativesize = *(cc + GET(cc, 1)) == OP_ALT ? 1 : 0;
-SLJIT_ASSERT(common->currententry->entry == NULL && common->recursive_head_ptr != 0);
-common->currententry->entry = LABEL();
-set_jumps(common->currententry->calls, common->currententry->entry);
+alt_max = no_alternatives(cc);
+alt_count = 0;
+
+/* Matching path. */
+SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0);
+common->currententry->entry_label = LABEL();
+set_jumps(common->currententry->entry_calls, common->currententry->entry_label);
sljit_emit_fast_enter(compiler, TMP2, 0);
count_match(common);
-allocate_stack(common, private_data_size + framesize + alternativesize);
-OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0);
-copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head);
+
+local_size = (alt_max > 1) ? 2 : 1;
+
+/* (Reversed) stack layout:
+ [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */
+
+allocate_stack(common, private_data_size + local_size);
+/* Save return address. */
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, has_quit);
+
+/* This variable is saved and restored all time when we enter or exit from a recursive context. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0);
+
if (needs_control_head)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0);
-if (needs_frame)
- init_frame(common, cc, NULL, framesize + alternativesize - 1, alternativesize, TRUE);
-if (alternativesize > 0)
+if (alt_max > 1)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
memset(&altbacktrack, 0, sizeof(backtrack_common));
@@ -10760,7 +11855,75 @@ while (1)
if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
return;
- add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP));
+ allocate_stack(common, (alt_max > 1 || has_accept) ? 2 : 1);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+
+ if (alt_max > 1 || has_accept)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count);
+
+ add_jump(compiler, &match, JUMP(SLJIT_JUMP));
+
+ if (alt_count == 0)
+ {
+ /* Backtracking path entry. */
+ SLJIT_ASSERT(common->currententry->backtrack_label == NULL);
+ common->currententry->backtrack_label = LABEL();
+ set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label);
+
+ sljit_emit_fast_enter(compiler, TMP1, 0);
+
+ if (has_accept)
+ accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_max * sizeof (sljit_sw));
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit);
+
+ if (alt_max > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+
+ if (alt_max > 4)
+ {
+ /* Table jump if alt_max is greater than 4. */
+ next_update_addr = allocate_read_only_data(common, alt_max * sizeof(sljit_uw));
+ if (SLJIT_UNLIKELY(next_update_addr == NULL))
+ return;
+ sljit_emit_ijump(compiler, SLJIT_JUMP, SLJIT_MEM1(TMP1), (sljit_sw)next_update_addr);
+ add_label_addr(common, next_update_addr++);
+ }
+ else
+ {
+ if (alt_max == 4)
+ alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw));
+ alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, sizeof(sljit_uw));
+ }
+ }
+ else
+ free_stack(common, has_accept ? 2 : 1);
+ }
+ else if (alt_max > 4)
+ add_label_addr(common, next_update_addr++);
+ else
+ {
+ if (alt_count != 2 * sizeof(sljit_uw))
+ {
+ JUMPHERE(alt1);
+ if (alt_max == 3 && alt_count == sizeof(sljit_uw))
+ alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw));
+ }
+ else
+ {
+ JUMPHERE(alt2);
+ if (alt_max == 4)
+ alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_uw));
+ }
+ }
+
+ alt_count += sizeof(sljit_uw);
compile_backtrackingpath(common, altbacktrack.top);
if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
@@ -10774,55 +11937,65 @@ while (1)
cc += GET(cc, 1);
}
-/* None of them matched. */
-OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
-jump = JUMP(SLJIT_JUMP);
+/* No alternative is matched. */
+
+quit = LABEL();
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, has_quit);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+free_stack(common, private_data_size + local_size);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+sljit_emit_fast_return(compiler, TMP2, 0);
if (common->quit != NULL)
{
+ SLJIT_ASSERT(has_quit);
+
set_jumps(common->quit, LABEL());
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
- if (needs_frame)
- {
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- }
- OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
- common->quit = NULL;
- add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, has_quit);
+ JUMPTO(SLJIT_JUMP, quit);
}
-set_jumps(common->accept, LABEL());
-OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
-if (needs_frame)
+if (has_accept)
{
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- }
-OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1);
+ JUMPHERE(accept_exit);
+ free_stack(common, 2);
-JUMPHERE(jump);
-if (common->quit != NULL)
- set_jumps(common->quit, LABEL());
-copy_private_data(common, ccbegin, ccend, FALSE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head);
-free_stack(common, private_data_size + framesize + alternativesize);
-if (needs_control_head)
- {
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 2 * sizeof(sljit_sw));
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP1, 0);
- OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0);
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, has_quit);
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+ free_stack(common, private_data_size + local_size);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ sljit_emit_fast_return(compiler, TMP2, 0);
}
-else
+
+if (common->accept != NULL)
{
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw));
- OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP2, 0);
+ SLJIT_ASSERT(has_accept);
+
+ set_jumps(common->accept, LABEL());
+
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count);
}
-sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0);
+
+set_jumps(match, LABEL());
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+sljit_emit_fast_return(compiler, TMP2, 0);
}
#undef COMPILE_BACKTRACKINGPATH
@@ -10854,11 +12027,13 @@ struct sljit_jump *jump;
struct sljit_jump *minlength_check_failed = NULL;
struct sljit_jump *reqbyte_notfound = NULL;
struct sljit_jump *empty_match = NULL;
+struct sljit_jump *end_anchor_failed = NULL;
SLJIT_ASSERT(tables);
memset(&rootbacktrack, 0, sizeof(backtrack_common));
memset(common, 0, sizeof(compiler_common));
+common->re = re;
common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code));
rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size;
@@ -11045,7 +12220,7 @@ if (!compiler)
common->compiler = compiler;
/* Main pcre_jit_exec entry. */
-sljit_emit_enter(compiler, 0, 1, 5, 5, 0, 0, private_data_size);
+sljit_emit_enter(compiler, 0, SLJIT_ARG1(SW), 5, 5, 0, 0, private_data_size);
/* Register init. */
reset_ovector(common, (re->top_bracket + 1) * 2);
@@ -11058,8 +12233,8 @@ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str))
OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end));
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match));
-OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base));
-OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit));
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start));
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0);
@@ -11076,7 +12251,7 @@ if (common->control_head_ptr != 0)
/* Main part of the matching */
if ((re->overall_options & PCRE2_ANCHORED) == 0)
{
- mainloop_label = mainloop_entry(common, (re->flags & PCRE2_HASCRORLF) != 0, re->overall_options);
+ mainloop_label = mainloop_entry(common);
continue_match_label = LABEL();
/* Forward search if possible. */
if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
@@ -11084,11 +12259,11 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0)
if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common))
;
else if ((re->flags & PCRE2_FIRSTSET) != 0)
- fast_forward_first_char(common, (PCRE2_UCHAR)(re->first_codeunit), (re->flags & PCRE2_FIRSTCASELESS) != 0);
+ fast_forward_first_char(common);
else if ((re->flags & PCRE2_STARTLINE) != 0)
fast_forward_newline(common);
else if ((re->flags & PCRE2_FIRSTMAPSET) != 0)
- fast_forward_start_bits(common, re->start_bitmap);
+ fast_forward_start_bits(common);
}
}
else
@@ -11135,6 +12310,9 @@ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
return PCRE2_ERROR_NOMEMORY;
}
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0);
+
if (common->might_be_empty)
{
empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
@@ -11147,15 +12325,26 @@ if (common->accept != NULL)
/* This means we have a match. Update the ovector. */
copy_ovector(common, re->top_bracket + 1);
-common->quit_label = common->forced_quit_label = LABEL();
+common->quit_label = common->abort_label = LABEL();
if (common->quit != NULL)
set_jumps(common->quit, common->quit_label);
-if (common->forced_quit != NULL)
- set_jumps(common->forced_quit, common->forced_quit_label);
+if (common->abort != NULL)
+ set_jumps(common->abort, common->abort_label);
if (minlength_check_failed != NULL)
- SET_LABEL(minlength_check_failed, common->forced_quit_label);
+ SET_LABEL(minlength_check_failed, common->abort_label);
sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0);
+if (common->failed_match != NULL)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+ set_jumps(common->failed_match, LABEL());
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ JUMPTO(SLJIT_JUMP, common->abort_label);
+ }
+
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ JUMPHERE(end_anchor_failed);
+
if (mode != PCRE2_JIT_COMPLETE)
{
common->partialmatchlabel = LABEL();
@@ -11236,9 +12425,9 @@ if (common->might_be_empty)
JUMPHERE(empty_match);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
JUMPTO(SLJIT_ZERO, empty_match_found_label);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label);
@@ -11249,7 +12438,7 @@ common->fast_forward_bc_ptr = NULL;
common->fast_fail_start_ptr = 0;
common->fast_fail_end_ptr = 0;
common->currententry = common->entries;
-common->local_exit = TRUE;
+common->local_quit_available = TRUE;
quit_label = common->quit_label;
while (common->currententry != NULL)
{
@@ -11266,7 +12455,7 @@ while (common->currententry != NULL)
flush_stubs(common);
common->currententry = common->currententry->next;
}
-common->local_exit = FALSE;
+common->local_quit_available = FALSE;
common->quit_label = quit_label;
/* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */
@@ -11274,20 +12463,23 @@ common->quit_label = quit_label;
set_jumps(common->stackalloc, LABEL());
/* RETURN_ADDR is not a saved register. */
sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0);
-OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0);
-OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE);
-sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize));
-jump = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
-OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
-OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top));
-OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit));
-OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
-sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE);
+OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0);
+
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize));
+
+jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0);
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+sljit_emit_fast_return(compiler, TMP1, 0);
/* Allocation failed. */
JUMPHERE(jump);
diff --git a/thirdparty/pcre2/src/pcre2_jit_match.c b/thirdparty/pcre2/src/pcre2_jit_match.c
index a323971ff3..5a66545bae 100644
--- a/thirdparty/pcre2/src/pcre2_jit_match.c
+++ b/thirdparty/pcre2/src/pcre2_jit_match.c
@@ -49,10 +49,10 @@ static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_f
sljit_u8 local_space[MACHINE_STACK_SIZE];
struct sljit_stack local_stack;
-local_stack.top = (sljit_sw)&local_space;
-local_stack.base = local_stack.top;
-local_stack.limit = local_stack.base + MACHINE_STACK_SIZE;
-local_stack.max_limit = local_stack.limit;
+local_stack.min_start = local_space;
+local_stack.start = local_space;
+local_stack.end = local_space + MACHINE_STACK_SIZE;
+local_stack.top = local_space + MACHINE_STACK_SIZE;
arguments->stack = &local_stack;
return executable_func(arguments);
}
@@ -118,7 +118,7 @@ if ((options & PCRE2_PARTIAL_HARD) != 0)
else if ((options & PCRE2_PARTIAL_SOFT) != 0)
index = 1;
-if (functions->executable_funcs[index] == NULL)
+if (functions == NULL || functions->executable_funcs[index] == NULL)
return PCRE2_ERROR_JIT_BADOPTION;
/* Sanity checks should be handled by pcre_exec. */
diff --git a/thirdparty/pcre2/src/pcre2_jit_test.c b/thirdparty/pcre2/src/pcre2_jit_test.c
deleted file mode 100644
index 705ba181eb..0000000000
--- a/thirdparty/pcre2/src/pcre2_jit_test.c
+++ /dev/null
@@ -1,1735 +0,0 @@
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <string.h>
-
-#define PCRE2_CODE_UNIT_WIDTH 0
-#include "pcre2.h"
-
-/*
- Letter characters:
- \xe6\x92\xad = 0x64ad = 25773 (kanji)
- Non-letter characters:
- \xc2\xa1 = 0xa1 = (Inverted Exclamation Mark)
- \xf3\xa9\xb7\x80 = 0xe9dc0 = 957888
- \xed\xa0\x80 = 55296 = 0xd800 (Invalid UTF character)
- \xed\xb0\x80 = 56320 = 0xdc00 (Invalid UTF character)
- Newlines:
- \xc2\x85 = 0x85 = 133 (NExt Line = NEL)
- \xe2\x80\xa8 = 0x2028 = 8232 (Line Separator)
- Othercase pairs:
- \xc3\xa9 = 0xe9 = 233 (e')
- \xc3\x89 = 0xc9 = 201 (E')
- \xc3\xa1 = 0xe1 = 225 (a')
- \xc3\x81 = 0xc1 = 193 (A')
- \x53 = 0x53 = S
- \x73 = 0x73 = s
- \xc5\xbf = 0x17f = 383 (long S)
- \xc8\xba = 0x23a = 570
- \xe2\xb1\xa5 = 0x2c65 = 11365
- \xe1\xbd\xb8 = 0x1f78 = 8056
- \xe1\xbf\xb8 = 0x1ff8 = 8184
- \xf0\x90\x90\x80 = 0x10400 = 66560
- \xf0\x90\x90\xa8 = 0x10428 = 66600
- \xc7\x84 = 0x1c4 = 452
- \xc7\x85 = 0x1c5 = 453
- \xc7\x86 = 0x1c6 = 454
- Caseless sets:
- ucp_Armenian - \x{531}-\x{556} -> \x{561}-\x{586}
- ucp_Coptic - \x{2c80}-\x{2ce3} -> caseless: XOR 0x1
- ucp_Latin - \x{ff21}-\x{ff3a} -> \x{ff41]-\x{ff5a}
-
- Mark property:
- \xcc\x8d = 0x30d = 781
- Special:
- \xc2\x80 = 0x80 = 128 (lowest 2 byte character)
- \xdf\xbf = 0x7ff = 2047 (highest 2 byte character)
- \xe0\xa0\x80 = 0x800 = 2048 (lowest 2 byte character)
- \xef\xbf\xbf = 0xffff = 65535 (highest 3 byte character)
- \xf0\x90\x80\x80 = 0x10000 = 65536 (lowest 4 byte character)
- \xf4\x8f\xbf\xbf = 0x10ffff = 1114111 (highest allowed utf character)
-*/
-
-static int regression_tests(void);
-
-int main(void)
-{
- int jit = 0;
-#if defined SUPPORT_PCRE2_8
- pcre2_config_8(PCRE2_CONFIG_JIT, &jit);
-#elif defined SUPPORT_PCRE2_16
- pcre2_config_16(PCRE2_CONFIG_JIT, &jit);
-#elif defined SUPPORT_PCRE2_32
- pcre2_config_32(PCRE2_CONFIG_JIT, &jit);
-#endif
- if (!jit) {
- printf("JIT must be enabled to run pcre_jit_test\n");
- return 1;
- }
- return regression_tests();
-}
-
-/* --------------------------------------------------------------------------------------- */
-
-#if !(defined SUPPORT_PCRE2_8) && !(defined SUPPORT_PCRE2_16) && !(defined SUPPORT_PCRE2_32)
-#error SUPPORT_PCRE2_8 or SUPPORT_PCRE2_16 or SUPPORT_PCRE2_32 must be defined
-#endif
-
-#define MU (PCRE2_MULTILINE | PCRE2_UTF)
-#define MUP (PCRE2_MULTILINE | PCRE2_UTF | PCRE2_UCP)
-#define CMU (PCRE2_CASELESS | PCRE2_MULTILINE | PCRE2_UTF)
-#define CMUP (PCRE2_CASELESS | PCRE2_MULTILINE | PCRE2_UTF | PCRE2_UCP)
-#define M (PCRE2_MULTILINE)
-#define MP (PCRE2_MULTILINE | PCRE2_UCP)
-#define U (PCRE2_UTF)
-#define CM (PCRE2_CASELESS | PCRE2_MULTILINE)
-
-#define BSR(x) ((x) << 16)
-#define A PCRE2_NEWLINE_ANYCRLF
-
-#define GET_NEWLINE(x) ((x) & 0xffff)
-#define GET_BSR(x) ((x) >> 16)
-
-#define OFFSET_MASK 0x00ffff
-#define F_NO8 0x010000
-#define F_NO16 0x020000
-#define F_NO32 0x020000
-#define F_NOMATCH 0x040000
-#define F_DIFF 0x080000
-#define F_FORCECONV 0x100000
-#define F_PROPERTY 0x200000
-
-struct regression_test_case {
- int compile_options;
- int newline;
- int match_options;
- int start_offset;
- const char *pattern;
- const char *input;
-};
-
-static struct regression_test_case regression_test_cases[] = {
- /* Constant strings. */
- { MU, A, 0, 0, "AbC", "AbAbC" },
- { MU, A, 0, 0, "ACCEPT", "AACACCACCEACCEPACCEPTACCEPTT" },
- { CMU, A, 0, 0, "aA#\xc3\xa9\xc3\x81", "aA#Aa#\xc3\x89\xc3\xa1" },
- { M, A, 0, 0, "[^a]", "aAbB" },
- { CM, A, 0, 0, "[^m]", "mMnN" },
- { M, A, 0, 0, "a[^b][^#]", "abacd" },
- { CM, A, 0, 0, "A[^B][^E]", "abacd" },
- { CMU, A, 0, 0, "[^x][^#]", "XxBll" },
- { MU, A, 0, 0, "[^a]", "aaa\xc3\xa1#Ab" },
- { CMU, A, 0, 0, "[^A]", "aA\xe6\x92\xad" },
- { MU, A, 0, 0, "\\W(\\W)?\\w", "\r\n+bc" },
- { MU, A, 0, 0, "\\W(\\W)?\\w", "\n\r+bc" },
- { MU, A, 0, 0, "\\W(\\W)?\\w", "\r\r+bc" },
- { MU, A, 0, 0, "\\W(\\W)?\\w", "\n\n+bc" },
- { MU, A, 0, 0, "[axd]", "sAXd" },
- { CMU, A, 0, 0, "[axd]", "sAXd" },
- { CMU, A, 0, 0 | F_NOMATCH, "[^axd]", "DxA" },
- { MU, A, 0, 0, "[a-dA-C]", "\xe6\x92\xad\xc3\xa9.B" },
- { MU, A, 0, 0, "[^a-dA-C]", "\xe6\x92\xad\xc3\xa9" },
- { CMU, A, 0, 0, "[^\xc3\xa9]", "\xc3\xa9\xc3\x89." },
- { MU, A, 0, 0, "[^\xc3\xa9]", "\xc3\xa9\xc3\x89." },
- { MU, A, 0, 0, "[^a]", "\xc2\x80[]" },
- { CMU, A, 0, 0, "\xf0\x90\x90\xa7", "\xf0\x90\x91\x8f" },
- { CM, A, 0, 0, "1a2b3c4", "1a2B3c51A2B3C4" },
- { PCRE2_CASELESS, 0, 0, 0, "\xff#a", "\xff#\xff\xfe##\xff#A" },
- { PCRE2_CASELESS, 0, 0, 0, "\xfe", "\xff\xfc#\xfe\xfe" },
- { PCRE2_CASELESS, 0, 0, 0, "a1", "Aa1" },
- { M, A, 0, 0, "\\Ca", "cda" },
- { CM, A, 0, 0, "\\Ca", "CDA" },
- { M, A, 0, 0 | F_NOMATCH, "\\Cx", "cda" },
- { CM, A, 0, 0 | F_NOMATCH, "\\Cx", "CDA" },
- { CMUP, A, 0, 0, "\xf0\x90\x90\x80\xf0\x90\x90\xa8", "\xf0\x90\x90\xa8\xf0\x90\x90\x80" },
- { CMUP, A, 0, 0, "\xf0\x90\x90\x80{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" },
- { CMUP, A, 0, 0, "\xf0\x90\x90\xa8{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" },
- { CMUP, A, 0, 0, "\xe1\xbd\xb8\xe1\xbf\xb8", "\xe1\xbf\xb8\xe1\xbd\xb8" },
- { M, A, 0, 0, "[3-57-9]", "5" },
-
- /* Assertions. */
- { MU, A, 0, 0, "\\b[^A]", "A_B#" },
- { M, A, 0, 0 | F_NOMATCH, "\\b\\W", "\n*" },
- { MU, A, 0, 0, "\\B[^,]\\b[^s]\\b", "#X" },
- { MP, A, 0, 0, "\\B", "_\xa1" },
- { MP, A, 0, 0 | F_PROPERTY, "\\b_\\b[,A]\\B", "_," },
- { MUP, A, 0, 0, "\\b", "\xe6\x92\xad!" },
- { MUP, A, 0, 0, "\\B", "_\xc2\xa1\xc3\xa1\xc2\x85" },
- { MUP, A, 0, 0, "\\b[^A]\\B[^c]\\b[^_]\\B", "_\xc3\xa1\xe2\x80\xa8" },
- { MUP, A, 0, 0, "\\b\\w+\\B", "\xc3\x89\xc2\xa1\xe6\x92\xad\xc3\x81\xc3\xa1" },
- { MU, A, 0, 0 | F_NOMATCH, "\\b.", "\xcd\xbe" },
- { CMUP, A, 0, 0, "\\By", "\xf0\x90\x90\xa8y" },
- { M, A, 0, 0 | F_NOMATCH, "\\R^", "\n" },
- { M, A, 0, 1 | F_NOMATCH, "^", "\n" },
- { 0, 0, 0, 0, "^ab", "ab" },
- { 0, 0, 0, 0 | F_NOMATCH, "^ab", "aab" },
- { M, PCRE2_NEWLINE_CRLF, 0, 0, "^a", "\r\raa\n\naa\r\naa" },
- { MU, A, 0, 0, "^-", "\xe2\x80\xa8--\xc2\x85-\r\n-" },
- { M, PCRE2_NEWLINE_ANY, 0, 0, "^-", "a--b--\x85--" },
- { MU, PCRE2_NEWLINE_ANY, 0, 0, "^-", "a--\xe2\x80\xa8--" },
- { MU, PCRE2_NEWLINE_ANY, 0, 0, "^-", "a--\xc2\x85--" },
- { 0, 0, 0, 0, "ab$", "ab" },
- { 0, 0, 0, 0 | F_NOMATCH, "ab$", "abab\n\n" },
- { PCRE2_DOLLAR_ENDONLY, 0, 0, 0 | F_NOMATCH, "ab$", "abab\r\n" },
- { M, PCRE2_NEWLINE_CRLF, 0, 0, "a$", "\r\raa\n\naa\r\naa" },
- { M, PCRE2_NEWLINE_ANY, 0, 0, "a$", "aaa" },
- { MU, PCRE2_NEWLINE_ANYCRLF, 0, 0, "#$", "#\xc2\x85###\r#" },
- { MU, PCRE2_NEWLINE_ANY, 0, 0, "#$", "#\xe2\x80\xa9" },
- { 0, PCRE2_NEWLINE_ANY, PCRE2_NOTBOL, 0 | F_NOMATCH, "^a", "aa\naa" },
- { M, PCRE2_NEWLINE_ANY, PCRE2_NOTBOL, 0, "^a", "aa\naa" },
- { 0, PCRE2_NEWLINE_ANY, PCRE2_NOTEOL, 0 | F_NOMATCH, "a$", "aa\naa" },
- { 0, PCRE2_NEWLINE_ANY, PCRE2_NOTEOL, 0 | F_NOMATCH, "a$", "aa\r\n" },
- { U | PCRE2_DOLLAR_ENDONLY, PCRE2_NEWLINE_ANY, 0, 0 | F_PROPERTY, "\\p{Any}{2,}$", "aa\r\n" },
- { M, PCRE2_NEWLINE_ANY, PCRE2_NOTEOL, 0, "a$", "aa\naa" },
- { 0, PCRE2_NEWLINE_CR, 0, 0, ".\\Z", "aaa" },
- { U, PCRE2_NEWLINE_CR, 0, 0, "a\\Z", "aaa\r" },
- { 0, PCRE2_NEWLINE_CR, 0, 0, ".\\Z", "aaa\n" },
- { 0, PCRE2_NEWLINE_CRLF, 0, 0, ".\\Z", "aaa\r" },
- { U, PCRE2_NEWLINE_CRLF, 0, 0, ".\\Z", "aaa\n" },
- { 0, PCRE2_NEWLINE_CRLF, 0, 0, ".\\Z", "aaa\r\n" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\r" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\n" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\r\n" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\xe2\x80\xa8" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\r" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\n" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".\\Z", "aaa\r\n" },
- { U, PCRE2_NEWLINE_ANY, 0, 0, ".\\Z", "aaa\xc2\x85" },
- { U, PCRE2_NEWLINE_ANY, 0, 0, ".\\Z", "aaa\xe2\x80\xa8" },
- { M, A, 0, 0, "\\Aa", "aaa" },
- { M, A, 0, 1 | F_NOMATCH, "\\Aa", "aaa" },
- { M, A, 0, 1, "\\Ga", "aaa" },
- { M, A, 0, 1 | F_NOMATCH, "\\Ga", "aba" },
- { M, A, 0, 0, "a\\z", "aaa" },
- { M, A, 0, 0 | F_NOMATCH, "a\\z", "aab" },
-
- /* Brackets and alternatives. */
- { MU, A, 0, 0, "(ab|bb|cd)", "bacde" },
- { MU, A, 0, 0, "(?:ab|a)(bc|c)", "ababc" },
- { MU, A, 0, 0, "((ab|(cc))|(bb)|(?:cd|efg))", "abac" },
- { CMU, A, 0, 0, "((aB|(Cc))|(bB)|(?:cd|EFg))", "AcCe" },
- { MU, A, 0, 0, "((ab|(cc))|(bb)|(?:cd|ebg))", "acebebg" },
- { MU, A, 0, 0, "(?:(a)|(?:b))(cc|(?:d|e))(a|b)k", "accabdbbccbk" },
- { MU, A, 0, 0, "\xc7\x82|\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" },
- { MU, A, 0, 0, "=\xc7\x82|#\xc6\x82", "\xf1\x83\x82\x82=\xc7\x82\xc7\x83" },
- { MU, A, 0, 0, "\xc7\x82\xc7\x83|\xc6\x82\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" },
- { MU, A, 0, 0, "\xc6\x82\xc6\x82|\xc7\x83\xc7\x83|\xc8\x84\xc8\x84", "\xf1\x83\x82\x82\xc8\x84\xc8\x84" },
-
- /* Greedy and non-greedy ? operators. */
- { MU, A, 0, 0, "(?:a)?a", "laab" },
- { CMU, A, 0, 0, "(A)?A", "llaab" },
- { MU, A, 0, 0, "(a)?\?a", "aab" }, /* ?? is the prefix of trygraphs in GCC. */
- { MU, A, 0, 0, "(a)?a", "manm" },
- { CMU, A, 0, 0, "(a|b)?\?d((?:e)?)", "ABABdx" },
- { MU, A, 0, 0, "(a|b)?\?d((?:e)?)", "abcde" },
- { MU, A, 0, 0, "((?:ab)?\?g|b(?:g(nn|d)?\?)?)?\?(?:n)?m", "abgnbgnnbgdnmm" },
-
- /* Greedy and non-greedy + operators */
- { MU, A, 0, 0, "(aa)+aa", "aaaaaaa" },
- { MU, A, 0, 0, "(aa)+?aa", "aaaaaaa" },
- { MU, A, 0, 0, "(?:aba|ab|a)+l", "ababamababal" },
- { MU, A, 0, 0, "(?:aba|ab|a)+?l", "ababamababal" },
- { MU, A, 0, 0, "(a(?:bc|cb|b|c)+?|ss)+e", "accssabccbcacbccbbXaccssabccbcacbccbbe" },
- { MU, A, 0, 0, "(a(?:bc|cb|b|c)+|ss)+?e", "accssabccbcacbccbbXaccssabccbcacbccbbe" },
- { MU, A, 0, 0, "(?:(b(c)+?)+)?\?(?:(bc)+|(cb)+)+(?:m)+", "bccbcccbcbccbcbPbccbcccbcbccbcbmmn" },
-
- /* Greedy and non-greedy * operators */
- { CMU, A, 0, 0, "(?:AA)*AB", "aaaaaaamaaaaaaab" },
- { MU, A, 0, 0, "(?:aa)*?ab", "aaaaaaamaaaaaaab" },
- { MU, A, 0, 0, "(aa|ab)*ab", "aaabaaab" },
- { CMU, A, 0, 0, "(aa|Ab)*?aB", "aaabaaab" },
- { MU, A, 0, 0, "(a|b)*(?:a)*(?:b)*m", "abbbaaababanabbbaaababamm" },
- { MU, A, 0, 0, "(a|b)*?(?:a)*?(?:b)*?m", "abbbaaababanabbbaaababamm" },
- { M, A, 0, 0, "a(a(\\1*)a|(b)b+){0}a", "aa" },
- { M, A, 0, 0, "((?:a|)*){0}a", "a" },
-
- /* Combining ? + * operators */
- { MU, A, 0, 0, "((bm)+)?\?(?:a)*(bm)+n|((am)+?)?(?:a)+(am)*n", "bmbmabmamaaamambmaman" },
- { MU, A, 0, 0, "(((ab)?cd)*ef)+g", "abcdcdefcdefefmabcdcdefcdefefgg" },
- { MU, A, 0, 0, "(((ab)?\?cd)*?ef)+?g", "abcdcdefcdefefmabcdcdefcdefefgg" },
- { MU, A, 0, 0, "(?:(ab)?c|(?:ab)+?d)*g", "ababcdccababddg" },
- { MU, A, 0, 0, "(?:(?:ab)?\?c|(ab)+d)*?g", "ababcdccababddg" },
-
- /* Single character iterators. */
- { MU, A, 0, 0, "(a+aab)+aaaab", "aaaabcaaaabaabcaabcaaabaaaab" },
- { MU, A, 0, 0, "(a*a*aab)+x", "aaaaabaabaaabmaabx" },
- { MU, A, 0, 0, "(a*?(b|ab)a*?)+x", "aaaabcxbbaabaacbaaabaabax" },
- { MU, A, 0, 0, "(a+(ab|ad)a+)+x", "aaabaaaadaabaaabaaaadaaax" },
- { MU, A, 0, 0, "(a?(a)a?)+(aaa)", "abaaabaaaaaaaa" },
- { MU, A, 0, 0, "(a?\?(a)a?\?)+(b)", "aaaacaaacaacacbaaab" },
- { MU, A, 0, 0, "(a{0,4}(b))+d", "aaaaaabaabcaaaaabaaaaabd" },
- { MU, A, 0, 0, "(a{0,4}?[^b])+d+(a{0,4}[^b])d+", "aaaaadaaaacaadddaaddd" },
- { MU, A, 0, 0, "(ba{2})+c", "baabaaabacbaabaac" },
- { MU, A, 0, 0, "(a*+bc++)+", "aaabbcaaabcccab" },
- { MU, A, 0, 0, "(a?+[^b])+", "babaacacb" },
- { MU, A, 0, 0, "(a{0,3}+b)(a{0,3}+b)(a{0,3}+)[^c]", "abaabaaacbaabaaaac" },
- { CMU, A, 0, 0, "([a-c]+[d-f]+?)+?g", "aBdacdehAbDaFgA" },
- { CMU, A, 0, 0, "[c-f]+k", "DemmFke" },
- { MU, A, 0, 0, "([DGH]{0,4}M)+", "GGDGHDGMMHMDHHGHM" },
- { MU, A, 0, 0, "([a-c]{4,}s)+", "abasabbasbbaabsbba" },
- { CMU, A, 0, 0, "[ace]{3,7}", "AcbDAcEEcEd" },
- { CMU, A, 0, 0, "[ace]{3,7}?", "AcbDAcEEcEd" },
- { CMU, A, 0, 0, "[ace]{3,}", "AcbDAcEEcEd" },
- { CMU, A, 0, 0, "[ace]{3,}?", "AcbDAcEEcEd" },
- { MU, A, 0, 0, "[ckl]{2,}?g", "cdkkmlglglkcg" },
- { CMU, A, 0, 0, "[ace]{5}?", "AcCebDAcEEcEd" },
- { MU, A, 0, 0, "([AbC]{3,5}?d)+", "BACaAbbAEAACCbdCCbdCCAAbb" },
- { MU, A, 0, 0, "([^ab]{0,}s){2}", "abaabcdsABamsDDs" },
- { MU, A, 0, 0, "\\b\\w+\\B", "x,a_cd" },
- { MUP, A, 0, 0, "\\b[^\xc2\xa1]+\\B", "\xc3\x89\xc2\xa1\xe6\x92\xad\xc3\x81\xc3\xa1" },
- { CMU, A, 0, 0, "[^b]+(a*)([^c]?d{3})", "aaaaddd" },
- { CMUP, A, 0, 0, "\xe1\xbd\xb8{2}", "\xe1\xbf\xb8#\xe1\xbf\xb8\xe1\xbd\xb8" },
- { CMU, A, 0, 0, "[^\xf0\x90\x90\x80]{2,4}@", "\xf0\x90\x90\xa8\xf0\x90\x90\x80###\xf0\x90\x90\x80@@@" },
- { CMU, A, 0, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" },
- { MU, A, 0, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" },
- { MU, A, 0, 0, "[^\xe1\xbd\xb8]{3,}?", "##\xe1\xbd\xb8#\xe1\xbd\xb8#\xc3\x89#\xe1\xbd\xb8" },
- { MU, A, 0, 0, "\\d+123", "987654321,01234" },
- { MU, A, 0, 0, "abcd*|\\w+xy", "aaaaa,abxyz" },
- { MU, A, 0, 0, "(?:abc|((?:amc|\\b\\w*xy)))", "aaaaa,abxyz" },
- { MU, A, 0, 0, "a(?R)|([a-z]++)#", ".abcd.abcd#."},
- { MU, A, 0, 0, "a(?R)|([a-z]++)#", ".abcd.mbcd#."},
- { MU, A, 0, 0, ".[ab]*.", "xx" },
- { MU, A, 0, 0, ".[ab]*a", "xxa" },
- { MU, A, 0, 0, ".[ab]?.", "xx" },
-
- /* Bracket repeats with limit. */
- { MU, A, 0, 0, "(?:(ab){2}){5}M", "abababababababababababM" },
- { MU, A, 0, 0, "(?:ab|abab){1,5}M", "abababababababababababM" },
- { MU, A, 0, 0, "(?>ab|abab){1,5}M", "abababababababababababM" },
- { MU, A, 0, 0, "(?:ab|abab){1,5}?M", "abababababababababababM" },
- { MU, A, 0, 0, "(?>ab|abab){1,5}?M", "abababababababababababM" },
- { MU, A, 0, 0, "(?:(ab){1,4}?){1,3}?M", "abababababababababababababM" },
- { MU, A, 0, 0, "(?:(ab){1,4}){1,3}abababababababababababM", "ababababababababababababM" },
- { MU, A, 0, 0 | F_NOMATCH, "(?:(ab){1,4}){1,3}abababababababababababM", "abababababababababababM" },
- { MU, A, 0, 0, "(ab){4,6}?M", "abababababababM" },
-
- /* Basic character sets. */
- { MU, A, 0, 0, "(?:\\s)+(?:\\S)+", "ab \t\xc3\xa9\xe6\x92\xad " },
- { MU, A, 0, 0, "(\\w)*(k)(\\W)?\?", "abcdef abck11" },
- { MU, A, 0, 0, "\\((\\d)+\\)\\D", "a() (83 (8)2 (9)ab" },
- { MU, A, 0, 0, "\\w(\\s|(?:\\d)*,)+\\w\\wb", "a 5, 4,, bb 5, 4,, aab" },
- { MU, A, 0, 0, "(\\v+)(\\V+)", "\x0e\xc2\x85\xe2\x80\xa8\x0b\x09\xe2\x80\xa9" },
- { MU, A, 0, 0, "(\\h+)(\\H+)", "\xe2\x80\xa8\xe2\x80\x80\x20\xe2\x80\x8a\xe2\x81\x9f\xe3\x80\x80\x09\x20\xc2\xa0\x0a" },
- { MU, A, 0, 0, "x[bcef]+", "xaxdxecbfg" },
- { MU, A, 0, 0, "x[bcdghij]+", "xaxexfxdgbjk" },
- { MU, A, 0, 0, "x[^befg]+", "xbxexacdhg" },
- { MU, A, 0, 0, "x[^bcdl]+", "xlxbxaekmd" },
- { MU, A, 0, 0, "x[^bcdghi]+", "xbxdxgxaefji" },
- { MU, A, 0, 0, "x[B-Fb-f]+", "xaxAxgxbfBFG" },
- { CMU, A, 0, 0, "\\x{e9}+", "#\xf0\x90\x90\xa8\xc3\xa8\xc3\xa9\xc3\x89\xc3\x88" },
- { CMU, A, 0, 0, "[^\\x{e9}]+", "\xc3\xa9#\xf0\x90\x90\xa8\xc3\xa8\xc3\x88\xc3\x89" },
- { MU, A, 0, 0, "[\\x02\\x7e]+", "\xc3\x81\xe1\xbf\xb8\xf0\x90\x90\xa8\x01\x02\x7e\x7f" },
- { MU, A, 0, 0, "[^\\x02\\x7e]+", "\x02\xc3\x81\xe1\xbf\xb8\xf0\x90\x90\xa8\x01\x7f\x7e" },
- { MU, A, 0, 0, "[\\x{81}-\\x{7fe}]+", "#\xe1\xbf\xb8\xf0\x90\x90\xa8\xc2\x80\xc2\x81\xdf\xbe\xdf\xbf" },
- { MU, A, 0, 0, "[^\\x{81}-\\x{7fe}]+", "\xc2\x81#\xe1\xbf\xb8\xf0\x90\x90\xa8\xc2\x80\xdf\xbf\xdf\xbe" },
- { MU, A, 0, 0, "[\\x{801}-\\x{fffe}]+", "#\xc3\xa9\xf0\x90\x90\x80\xe0\xa0\x80\xe0\xa0\x81\xef\xbf\xbe\xef\xbf\xbf" },
- { MU, A, 0, 0, "[^\\x{801}-\\x{fffe}]+", "\xe0\xa0\x81#\xc3\xa9\xf0\x90\x90\x80\xe0\xa0\x80\xef\xbf\xbf\xef\xbf\xbe" },
- { MU, A, 0, 0, "[\\x{10001}-\\x{10fffe}]+", "#\xc3\xa9\xe2\xb1\xa5\xf0\x90\x80\x80\xf0\x90\x80\x81\xf4\x8f\xbf\xbe\xf4\x8f\xbf\xbf" },
- { MU, A, 0, 0, "[^\\x{10001}-\\x{10fffe}]+", "\xf0\x90\x80\x81#\xc3\xa9\xe2\xb1\xa5\xf0\x90\x80\x80\xf4\x8f\xbf\xbf\xf4\x8f\xbf\xbe" },
-
- /* Unicode properties. */
- { MUP, A, 0, 0, "[1-5\xc3\xa9\\w]", "\xc3\xa1_" },
- { MUP, A, 0, 0 | F_PROPERTY, "[\xc3\x81\\p{Ll}]", "A_\xc3\x89\xc3\xa1" },
- { MUP, A, 0, 0, "[\\Wd-h_x-z]+", "a\xc2\xa1#_yhzdxi" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[\\P{Any}]", "abc" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[^\\p{Any}]", "abc" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[\\P{Any}\xc3\xa1-\xc3\xa8]", "abc" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[^\\p{Any}\xc3\xa1-\xc3\xa8]", "abc" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[\xc3\xa1-\xc3\xa8\\P{Any}]", "abc" },
- { MUP, A, 0, 0 | F_NOMATCH | F_PROPERTY, "[^\xc3\xa1-\xc3\xa8\\p{Any}]", "abc" },
- { MUP, A, 0, 0 | F_PROPERTY, "[\xc3\xa1-\xc3\xa8\\p{Any}]", "abc" },
- { MUP, A, 0, 0 | F_PROPERTY, "[^\xc3\xa1-\xc3\xa8\\P{Any}]", "abc" },
- { MUP, A, 0, 0, "[b-\xc3\xa9\\s]", "a\xc\xe6\x92\xad" },
- { CMUP, A, 0, 0, "[\xc2\x85-\xc2\x89\xc3\x89]", "\xc2\x84\xc3\xa9" },
- { MUP, A, 0, 0, "[^b-d^&\\s]{3,}", "db^ !a\xe2\x80\xa8_ae" },
- { MUP, A, 0, 0 | F_PROPERTY, "[^\\S\\P{Any}][\\sN]{1,3}[\\P{N}]{4}", "\xe2\x80\xaa\xa N\x9\xc3\xa9_0" },
- { MU, A, 0, 0 | F_PROPERTY, "[^\\P{L}\x9!D-F\xa]{2,3}", "\x9,.DF\xa.CG\xc3\x81" },
- { CMUP, A, 0, 0, "[\xc3\xa1-\xc3\xa9_\xe2\x80\xa0-\xe2\x80\xaf]{1,5}[^\xe2\x80\xa0-\xe2\x80\xaf]", "\xc2\xa1\xc3\x89\xc3\x89\xe2\x80\xaf_\xe2\x80\xa0" },
- { MUP, A, 0, 0 | F_PROPERTY, "[\xc3\xa2-\xc3\xa6\xc3\x81-\xc3\x84\xe2\x80\xa8-\xe2\x80\xa9\xe6\x92\xad\\p{Zs}]{2,}", "\xe2\x80\xa7\xe2\x80\xa9\xe6\x92\xad \xe6\x92\xae" },
- { MUP, A, 0, 0 | F_PROPERTY, "[\\P{L&}]{2}[^\xc2\x85-\xc2\x89\\p{Ll}\\p{Lu}]{2}", "\xc3\xa9\xe6\x92\xad.a\xe6\x92\xad|\xc2\x8a#" },
- { PCRE2_UCP, 0, 0, 0 | F_PROPERTY, "[a-b\\s]{2,5}[^a]", "AB baaa" },
-
- /* Possible empty brackets. */
- { MU, A, 0, 0, "(?:|ab||bc|a)+d", "abcxabcabd" },
- { MU, A, 0, 0, "(|ab||bc|a)+d", "abcxabcabd" },
- { MU, A, 0, 0, "(?:|ab||bc|a)*d", "abcxabcabd" },
- { MU, A, 0, 0, "(|ab||bc|a)*d", "abcxabcabd" },
- { MU, A, 0, 0, "(?:|ab||bc|a)+?d", "abcxabcabd" },
- { MU, A, 0, 0, "(|ab||bc|a)+?d", "abcxabcabd" },
- { MU, A, 0, 0, "(?:|ab||bc|a)*?d", "abcxabcabd" },
- { MU, A, 0, 0, "(|ab||bc|a)*?d", "abcxabcabd" },
- { MU, A, 0, 0, "(((a)*?|(?:ba)+)+?|(?:|c|ca)*)*m", "abaacaccabacabalabaacaccabacabamm" },
- { MU, A, 0, 0, "(?:((?:a)*|(ba)+?)+|(|c|ca)*?)*?m", "abaacaccabacabalabaacaccabacabamm" },
-
- /* Start offset. */
- { MU, A, 0, 3, "(\\d|(?:\\w)*\\w)+", "0ac01Hb" },
- { MU, A, 0, 4 | F_NOMATCH, "(\\w\\W\\w)+", "ab#d" },
- { MU, A, 0, 2 | F_NOMATCH, "(\\w\\W\\w)+", "ab#d" },
- { MU, A, 0, 1, "(\\w\\W\\w)+", "ab#d" },
-
- /* Newline. */
- { M, PCRE2_NEWLINE_CRLF, 0, 0, "\\W{0,2}[^#]{3}", "\r\n#....." },
- { M, PCRE2_NEWLINE_CR, 0, 0, "\\W{0,2}[^#]{3}", "\r\n#....." },
- { M, PCRE2_NEWLINE_CRLF, 0, 0, "\\W{1,3}[^#]", "\r\n##...." },
- { MU, A, PCRE2_NO_UTF_CHECK, 1, "^.a", "\n\x80\nxa" },
- { MU, A, 0, 1, "^", "\r\n" },
- { M, PCRE2_NEWLINE_CRLF, 0, 1 | F_NOMATCH, "^", "\r\n" },
- { M, PCRE2_NEWLINE_CRLF, 0, 1, "^", "\r\na" },
-
- /* Any character except newline or any newline. */
- { 0, PCRE2_NEWLINE_CRLF, 0, 0, ".", "\r" },
- { U, PCRE2_NEWLINE_CRLF, 0, 0, ".(.).", "a\xc3\xa1\r\n\n\r\r" },
- { 0, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".(.)", "a\rb\nc\r\n\xc2\x85\xe2\x80\xa8" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0, ".(.)", "a\rb\nc\r\n\xc2\x85\xe2\x80\xa8" },
- { U, PCRE2_NEWLINE_ANY, 0, 0, "(.).", "a\rb\nc\r\n\xc2\x85\xe2\x80\xa9$de" },
- { U, PCRE2_NEWLINE_ANYCRLF, 0, 0 | F_NOMATCH, ".(.).", "\xe2\x80\xa8\nb\r" },
- { 0, PCRE2_NEWLINE_ANY, 0, 0, "(.)(.)", "#\x85#\r#\n#\r\n#\x84" },
- { U, PCRE2_NEWLINE_ANY, 0, 0, "(.+)#", "#\rMn\xc2\x85#\n###" },
- { 0, BSR(PCRE2_BSR_ANYCRLF), 0, 0, "\\R", "\r" },
- { 0, BSR(PCRE2_BSR_ANYCRLF), 0, 0, "\\R", "\x85#\r\n#" },
- { U, BSR(PCRE2_BSR_UNICODE), 0, 0, "\\R", "ab\xe2\x80\xa8#c" },
- { U, BSR(PCRE2_BSR_UNICODE), 0, 0, "\\R", "ab\r\nc" },
- { U, PCRE2_NEWLINE_CRLF | BSR(PCRE2_BSR_UNICODE), 0, 0, "(\\R.)+", "\xc2\x85\r\n#\xe2\x80\xa8\n\r\n\r" },
- { MU, A, 0, 0 | F_NOMATCH, "\\R+", "ab" },
- { MU, A, 0, 0, "\\R+", "ab\r\n\r" },
- { MU, A, 0, 0, "\\R*", "ab\r\n\r" },
- { MU, A, 0, 0, "\\R*", "\r\n\r" },
- { MU, A, 0, 0, "\\R{2,4}", "\r\nab\r\r" },
- { MU, A, 0, 0, "\\R{2,4}", "\r\nab\n\n\n\r\r\r" },
- { MU, A, 0, 0, "\\R{2,}", "\r\nab\n\n\n\r\r\r" },
- { MU, A, 0, 0, "\\R{0,3}", "\r\n\r\n\r\n\r\n\r\n" },
- { MU, A, 0, 0 | F_NOMATCH, "\\R+\\R\\R", "\r\n\r\n" },
- { MU, A, 0, 0, "\\R+\\R\\R", "\r\r\r" },
- { MU, A, 0, 0, "\\R*\\R\\R", "\n\r" },
- { MU, A, 0, 0 | F_NOMATCH, "\\R{2,4}\\R\\R", "\r\r\r" },
- { MU, A, 0, 0, "\\R{2,4}\\R\\R", "\r\r\r\r" },
-
- /* Atomic groups (no fallback from "next" direction). */
- { MU, A, 0, 0 | F_NOMATCH, "(?>ab)ab", "bab" },
- { MU, A, 0, 0 | F_NOMATCH, "(?>(ab))ab", "bab" },
- { MU, A, 0, 0, "(?>ab)+abc(?>de)*def(?>gh)?ghe(?>ij)+?k(?>lm)*?n(?>op)?\?op",
- "bababcdedefgheijijklmlmnop" },
- { MU, A, 0, 0, "(?>a(b)+a|(ab)?\?(b))an", "abban" },
- { MU, A, 0, 0, "(?>ab+a|(?:ab)?\?b)an", "abban" },
- { MU, A, 0, 0, "((?>ab|ad|)*?)(?>|c)*abad", "abababcababad" },
- { MU, A, 0, 0, "(?>(aa|b|)*+(?>(##)|###)*d|(aa)(?>(baa)?)m)", "aabaa#####da" },
- { MU, A, 0, 0, "((?>a|)+?)b", "aaacaaab" },
- { MU, A, 0, 0, "(?>x|)*$", "aaa" },
- { MU, A, 0, 0, "(?>(x)|)*$", "aaa" },
- { MU, A, 0, 0, "(?>x|())*$", "aaa" },
- { MU, A, 0, 0, "((?>[cxy]a|[a-d])*?)b", "aaa+ aaab" },
- { MU, A, 0, 0, "((?>[cxy](a)|[a-d])*?)b", "aaa+ aaab" },
- { MU, A, 0, 0, "(?>((?>(a+))))bab|(?>((?>(a+))))bb", "aaaabaaabaabab" },
- { MU, A, 0, 0, "(?>(?>a+))bab|(?>(?>a+))bb", "aaaabaaabaabab" },
- { MU, A, 0, 0, "(?>(a)c|(?>(c)|(a))a)b*?bab", "aaaabaaabaabab" },
- { MU, A, 0, 0, "(?>ac|(?>c|a)a)b*?bab", "aaaabaaabaabab" },
- { MU, A, 0, 0, "(?>(b)b|(a))*b(?>(c)|d)?x", "ababcaaabdbx" },
- { MU, A, 0, 0, "(?>bb|a)*b(?>c|d)?x", "ababcaaabdbx" },
- { MU, A, 0, 0, "(?>(bb)|a)*b(?>c|(d))?x", "ababcaaabdbx" },
- { MU, A, 0, 0, "(?>(a))*?(?>(a))+?(?>(a))??x", "aaaaaacccaaaaabax" },
- { MU, A, 0, 0, "(?>a)*?(?>a)+?(?>a)??x", "aaaaaacccaaaaabax" },
- { MU, A, 0, 0, "(?>(a)|)*?(?>(a)|)+?(?>(a)|)??x", "aaaaaacccaaaaabax" },
- { MU, A, 0, 0, "(?>a|)*?(?>a|)+?(?>a|)??x", "aaaaaacccaaaaabax" },
- { MU, A, 0, 0, "(?>a(?>(a{0,2}))*?b|aac)+b", "aaaaaaacaaaabaaaaacaaaabaacaaabb" },
- { CM, A, 0, 0, "(?>((?>a{32}|b+|(a*))?(?>c+|d*)?\?)+e)+?f", "aaccebbdde bbdaaaccebbdee bbdaaaccebbdeef" },
- { MU, A, 0, 0, "(?>(?:(?>aa|a||x)+?b|(?>aa|a||(x))+?c)?(?>[ad]{0,2})*?d)+d", "aaacdbaabdcabdbaaacd aacaabdbdcdcaaaadaabcbaadd" },
- { MU, A, 0, 0, "(?>(?:(?>aa|a||(x))+?b|(?>aa|a||x)+?c)?(?>[ad]{0,2})*?d)+d", "aaacdbaabdcabdbaaacd aacaabdbdcdcaaaadaabcbaadd" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X", "\xcc\x8d\xcc\x8d" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X", "\xcc\x8d\xcc\x8d#\xcc\x8d\xcc\x8d" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X+..", "\xcc\x8d#\xcc\x8d#\xcc\x8d\xcc\x8d" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X{2,4}", "abcdef" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X{2,4}?", "abcdef" },
- { MU, A, 0, 0 | F_NOMATCH | F_PROPERTY, "\\X{2,4}..", "#\xcc\x8d##" },
- { MU, A, 0, 0 | F_PROPERTY, "\\X{2,4}..", "#\xcc\x8d#\xcc\x8d##" },
- { MU, A, 0, 0, "(c(ab)?+ab)+", "cabcababcab" },
- { MU, A, 0, 0, "(?>(a+)b)+aabab", "aaaabaaabaabab" },
-
- /* Possessive quantifiers. */
- { MU, A, 0, 0, "(?:a|b)++m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(?:a|b)*+m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(?:a|b)*+m", "ababbaaxababbaam" },
- { MU, A, 0, 0, "(a|b)++m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(a|b)*+m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(a|b)*+m", "ababbaaxababbaam" },
- { MU, A, 0, 0, "(a|b(*ACCEPT))++m", "maaxab" },
- { MU, A, 0, 0, "(?:b*)++m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(?:b*)++m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "(?:b*)*+m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(?:b*)*+m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "(b*)++m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(b*)++m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "(b*)*+m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(b*)*+m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "(?:a|(b))++m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(?:(a)|b)*+m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "(?:(a)|(b))*+m", "ababbaaxababbaam" },
- { MU, A, 0, 0, "(a|(b))++m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "((a)|b)*+m", "mababbaaxababbaam" },
- { MU, A, 0, 0, "((a)|(b))*+m", "ababbaaxababbaam" },
- { MU, A, 0, 0, "(a|(b)(*ACCEPT))++m", "maaxab" },
- { MU, A, 0, 0, "(?:(b*))++m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(?:(b*))++m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "(?:(b*))*+m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "(?:(b*))*+m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "((b*))++m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "((b*))++m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0, "((b*))*+m", "bxbbxbbbxm" },
- { MU, A, 0, 0, "((b*))*+m", "bxbbxbbbxbbm" },
- { MU, A, 0, 0 | F_NOMATCH, "(?>(b{2,4}))(?:(?:(aa|c))++m|(?:(aa|c))+n)", "bbaacaaccaaaacxbbbmbn" },
- { MU, A, 0, 0, "((?:b)++a)+(cd)*+m", "bbababbacdcdnbbababbacdcdm" },
- { MU, A, 0, 0, "((?:(b))++a)+((c)d)*+m", "bbababbacdcdnbbababbacdcdm" },
- { MU, A, 0, 0, "(?:(?:(?:ab)*+k)++(?:n(?:cd)++)*+)*+m", "ababkkXababkkabkncXababkkabkncdcdncdXababkkabkncdcdncdkkabkncdXababkkabkncdcdncdkkabkncdm" },
- { MU, A, 0, 0, "(?:((ab)*+(k))++(n(?:c(d))++)*+)*+m", "ababkkXababkkabkncXababkkabkncdcdncdXababkkabkncdcdncdkkabkncdXababkkabkncdcdncdkkabkncdm" },
-
- /* Back references. */
- { MU, A, 0, 0, "(aa|bb)(\\1*)(ll|)(\\3*)bbbbbbc", "aaaaaabbbbbbbbc" },
- { CMU, A, 0, 0, "(aa|bb)(\\1+)(ll|)(\\3+)bbbbbbc", "bBbbBbCbBbbbBbbcbbBbbbBBbbC" },
- { CM, A, 0, 0, "(a{2,4})\\1", "AaAaaAaA" },
- { MU, A, 0, 0, "(aa|bb)(\\1?)aa(\\1?)(ll|)(\\4+)bbc", "aaaaaaaabbaabbbbaabbbbc" },
- { MU, A, 0, 0, "(aa|bb)(\\1{0,5})(ll|)(\\3{0,5})cc", "bbxxbbbbxxaaaaaaaaaaaaaaaacc" },
- { MU, A, 0, 0, "(aa|bb)(\\1{3,5})(ll|)(\\3{3,5})cc", "bbbbbbbbbbbbaaaaaaccbbbbbbbbbbbbbbcc" },
- { MU, A, 0, 0, "(aa|bb)(\\1{3,})(ll|)(\\3{3,})cc", "bbbbbbbbbbbbaaaaaaccbbbbbbbbbbbbbbcc" },
- { MU, A, 0, 0, "(\\w+)b(\\1+)c", "GabGaGaDbGaDGaDc" },
- { MU, A, 0, 0, "(?:(aa)|b)\\1?b", "bb" },
- { CMU, A, 0, 0, "(aa|bb)(\\1*?)aa(\\1+?)", "bBBbaaAAaaAAaa" },
- { MU, A, 0, 0, "(aa|bb)(\\1*?)(dd|)cc(\\3+?)", "aaaaaccdd" },
- { CMU, A, 0, 0, "(?:(aa|bb)(\\1?\?)cc){2}(\\1?\?)", "aAaABBbbAAaAcCaAcCaA" },
- { MU, A, 0, 0, "(?:(aa|bb)(\\1{3,5}?)){2}(dd|)(\\3{3,5}?)", "aaaaaabbbbbbbbbbaaaaaaaaaaaaaa" },
- { CM, A, 0, 0, "(?:(aa|bb)(\\1{3,}?)){2}(dd|)(\\3{3,}?)", "aaaaaabbbbbbbbbbaaaaaaaaaaaaaa" },
- { MU, A, 0, 0, "(?:(aa|bb)(\\1{0,3}?)){2}(dd|)(\\3{0,3}?)b(\\1{0,3}?)(\\1{0,3})", "aaaaaaaaaaaaaaabaaaaa" },
- { MU, A, 0, 0, "(a(?:\\1|)a){3}b", "aaaaaaaaaaab" },
- { M, A, 0, 0, "(a?)b(\\1\\1*\\1+\\1?\\1*?\\1+?\\1??\\1*+\\1++\\1?+\\1{4}\\1{3,5}\\1{4,}\\1{0,5}\\1{3,5}?\\1{4,}?\\1{0,5}?\\1{3,5}+\\1{4,}+\\1{0,5}+#){2}d", "bb#b##d" },
- { MUP, A, 0, 0 | F_PROPERTY, "(\\P{N})\\1{2,}", ".www." },
- { MUP, A, 0, 0 | F_PROPERTY, "(\\P{N})\\1{0,2}", "wwwww." },
- { MUP, A, 0, 0 | F_PROPERTY, "(\\P{N})\\1{1,2}ww", "wwww" },
- { MUP, A, 0, 0 | F_PROPERTY, "(\\P{N})\\1{1,2}ww", "wwwww" },
- { PCRE2_UCP, 0, 0, 0 | F_PROPERTY, "(\\P{N})\\1{2,}", ".www." },
- { CMUP, A, 0, 0, "(\xf0\x90\x90\x80)\\1", "\xf0\x90\x90\xa8\xf0\x90\x90\xa8" },
- { MU | PCRE2_DUPNAMES, A, 0, 0 | F_NOMATCH, "\\k<A>{1,3}(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES | PCRE2_MATCH_UNSET_BACKREF, A, 0, 0, "\\k<A>{1,3}(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES | PCRE2_MATCH_UNSET_BACKREF, A, 0, 0, "\\k<A>*(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?<A>aa)(?<A>bb)\\k<A>{0,3}aaaaaa", "aabbaaaaaa" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?<A>aa)(?<A>bb)\\k<A>{2,5}bb", "aabbaaaabb" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>aa)|(?<A>bb))\\k<A>{0,3}m", "aaaaaaaabbbbaabbbbm" },
- { MU | PCRE2_DUPNAMES, A, 0, 0 | F_NOMATCH, "\\k<A>{1,3}?(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES | PCRE2_MATCH_UNSET_BACKREF, A, 0, 0, "\\k<A>{1,3}?(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "\\k<A>*?(?<A>aa)(?<A>bb)", "aabb" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>aa)|(?<A>bb))\\k<A>{0,3}?m", "aaaaaabbbbbbaabbbbbbbbbbm" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>aa)|(?<A>bb))\\k<A>*?m", "aaaaaabbbbbbaabbbbbbbbbbm" },
- { MU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>aa)|(?<A>bb))\\k<A>{2,3}?", "aaaabbbbaaaabbbbbbbbbb" },
- { CMU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>AA)|(?<A>BB))\\k<A>{0,3}M", "aaaaaaaabbbbaabbbbm" },
- { CMU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>AA)|(?<A>BB))\\k<A>{1,3}M", "aaaaaaaabbbbaabbbbm" },
- { CMU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>AA)|(?<A>BB))\\k<A>{0,3}?M", "aaaaaabbbbbbaabbbbbbbbbbm" },
- { CMU | PCRE2_DUPNAMES, A, 0, 0, "(?:(?<A>AA)|(?<A>BB))\\k<A>{2,3}?", "aaaabbbbaaaabbbbbbbbbb" },
-
- /* Assertions. */
- { MU, A, 0, 0, "(?=xx|yy|zz)\\w{4}", "abczzdefg" },
- { MU, A, 0, 0, "(?=((\\w+)b){3}|ab)", "dbbbb ab" },
- { MU, A, 0, 0, "(?!ab|bc|cd)[a-z]{2}", "Xabcdef" },
- { MU, A, 0, 0, "(?<=aaa|aa|a)a", "aaa" },
- { MU, A, 0, 2, "(?<=aaa|aa|a)a", "aaa" },
- { M, A, 0, 0, "(?<=aaa|aa|a)a", "aaa" },
- { M, A, 0, 2, "(?<=aaa|aa|a)a", "aaa" },
- { MU, A, 0, 0, "(\\d{2})(?!\\w+c|(((\\w?)m){2}n)+|\\1)", "x5656" },
- { MU, A, 0, 0, "((?=((\\d{2,6}\\w){2,}))\\w{5,20}K){2,}", "567v09708K12l00M00 567v09708K12l00M00K45K" },
- { MU, A, 0, 0, "(?=(?:(?=\\S+a)\\w*(b)){3})\\w+\\d", "bba bbab nbbkba nbbkba0kl" },
- { MU, A, 0, 0, "(?>a(?>(b+))a(?=(..)))*?k", "acabbcabbaabacabaabbakk" },
- { MU, A, 0, 0, "((?(?=(a))a)+k)", "bbak" },
- { MU, A, 0, 0, "((?(?=a)a)+k)", "bbak" },
- { MU, A, 0, 0 | F_NOMATCH, "(?=(?>(a))m)amk", "a k" },
- { MU, A, 0, 0 | F_NOMATCH, "(?!(?>(a))m)amk", "a k" },
- { MU, A, 0, 0 | F_NOMATCH, "(?>(?=(a))am)amk", "a k" },
- { MU, A, 0, 0, "(?=(?>a|(?=(?>(b+))a|c)[a-c]+)*?m)[a-cm]+k", "aaam bbam baaambaam abbabba baaambaamk" },
- { MU, A, 0, 0, "(?> ?\?\\b(?(?=\\w{1,4}(a))m)\\w{0,8}bc){2,}?", "bca ssbc mabd ssbc mabc" },
- { MU, A, 0, 0, "(?:(?=ab)?[^n][^n])+m", "ababcdabcdcdabnababcdabcdcdabm" },
- { MU, A, 0, 0, "(?:(?=a(b))?[^n][^n])+m", "ababcdabcdcdabnababcdabcdcdabm" },
- { MU, A, 0, 0, "(?:(?=.(.))??\\1.)+m", "aabbbcbacccanaabbbcbacccam" },
- { MU, A, 0, 0, "(?:(?=.)??[a-c])+m", "abacdcbacacdcaccam" },
- { MU, A, 0, 0, "((?!a)?(?!([^a]))?)+$", "acbab" },
- { MU, A, 0, 0, "((?!a)?\?(?!([^a]))?\?)+$", "acbab" },
- { MU, A, 0, 0, "a(?=(?C)\\B(?C`x`))b", "ab" },
- { MU, A, 0, 0, "a(?!(?C)\\B(?C`x`))bb|ab", "abb" },
- { MU, A, 0, 0, "a(?=\\b|(?C)\\B(?C`x`))b", "ab" },
- { MU, A, 0, 0, "a(?!\\b|(?C)\\B(?C`x`))bb|ab", "abb" },
- { MU, A, 0, 0, "c(?(?=(?C)\\B(?C`x`))ab|a)", "cab" },
- { MU, A, 0, 0, "c(?(?!(?C)\\B(?C`x`))ab|a)", "cab" },
- { MU, A, 0, 0, "c(?(?=\\b|(?C)\\B(?C`x`))ab|a)", "cab" },
- { MU, A, 0, 0, "c(?(?!\\b|(?C)\\B(?C`x`))ab|a)", "cab" },
- { MU, A, 0, 0, "a(?=)b", "ab" },
- { MU, A, 0, 0 | F_NOMATCH, "a(?!)b", "ab" },
-
- /* Not empty, ACCEPT, FAIL */
- { MU, A, PCRE2_NOTEMPTY, 0 | F_NOMATCH, "a*", "bcx" },
- { MU, A, PCRE2_NOTEMPTY, 0, "a*", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY, 0, "a*?", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY_ATSTART, 0, "a*", "bcaad" },
- { MU, A, 0, 0, "a(*ACCEPT)b", "ab" },
- { MU, A, PCRE2_NOTEMPTY, 0 | F_NOMATCH, "a*(*ACCEPT)b", "bcx" },
- { MU, A, PCRE2_NOTEMPTY, 0, "a*(*ACCEPT)b", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY, 0, "a*?(*ACCEPT)b", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY, 0 | F_NOMATCH, "(?:z|a*(*ACCEPT)b)", "bcx" },
- { MU, A, PCRE2_NOTEMPTY, 0, "(?:z|a*(*ACCEPT)b)", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY, 0, "(?:z|a*?(*ACCEPT)b)", "bcaad" },
- { MU, A, PCRE2_NOTEMPTY_ATSTART, 0, "a*(*ACCEPT)b", "bcx" },
- { MU, A, PCRE2_NOTEMPTY_ATSTART, 0 | F_NOMATCH, "a*(*ACCEPT)b", "" },
- { MU, A, 0, 0, "((a(*ACCEPT)b))", "ab" },
- { MU, A, 0, 0, "(a(*FAIL)a|a)", "aaa" },
- { MU, A, 0, 0, "(?=ab(*ACCEPT)b)a", "ab" },
- { MU, A, 0, 0, "(?=(?:x|ab(*ACCEPT)b))", "ab" },
- { MU, A, 0, 0, "(?=(a(b(*ACCEPT)b)))a", "ab" },
- { MU, A, PCRE2_NOTEMPTY, 0, "(?=a*(*ACCEPT))c", "c" },
-
- /* Conditional blocks. */
- { MU, A, 0, 0, "(?(?=(a))a|b)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?!(b))a|b)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?=a)a|b)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?!b)a|b)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?=(a))a*|b*)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?!(b))a*|b*)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?!(b))(?:aaaaaa|a)|(?:bbbbbb|b))+aaaak", "aaaaaaaaaaaaaa bbbbbbbbbbbbbbb aaaaaaak" },
- { MU, A, 0, 0, "(?(?!b)(?:aaaaaa|a)|(?:bbbbbb|b))+aaaak", "aaaaaaaaaaaaaa bbbbbbbbbbbbbbb aaaaaaak" },
- { MU, A, 0, 0 | F_DIFF, "(?(?!(b))(?:aaaaaa|a)|(?:bbbbbb|b))+bbbbk", "aaaaaaaaaaaaaa bbbbbbbbbbbbbbb bbbbbbbk" },
- { MU, A, 0, 0, "(?(?!b)(?:aaaaaa|a)|(?:bbbbbb|b))+bbbbk", "aaaaaaaaaaaaaa bbbbbbbbbbbbbbb bbbbbbbk" },
- { MU, A, 0, 0, "(?(?=a)a*|b*)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?!b)a*|b*)+k", "ababbalbbadabak" },
- { MU, A, 0, 0, "(?(?=a)ab)", "a" },
- { MU, A, 0, 0, "(?(?<!b)c)", "b" },
- { MU, A, 0, 0, "(?(DEFINE)a(b))", "a" },
- { MU, A, 0, 0, "a(?(DEFINE)(?:b|(?:c?)+)*)", "a" },
- { MU, A, 0, 0, "(?(?=.[a-c])[k-l]|[A-D])", "kdB" },
- { MU, A, 0, 0, "(?(?!.{0,4}[cd])(aa|bb)|(cc|dd))+", "aabbccddaa" },
- { MU, A, 0, 0, "(?(?=[^#@]*@)(aaab|aa|aba)|(aba|aab)){3,}", "aaabaaaba#aaabaaaba#aaabaaaba@" },
- { MU, A, 0, 0, "((?=\\w{5})\\w(?(?=\\w*k)\\d|[a-f_])*\\w\\s)+", "mol m10kk m088k _f_a_ mbkkl" },
- { MU, A, 0, 0, "(c)?\?(?(1)a|b)", "cdcaa" },
- { MU, A, 0, 0, "(c)?\?(?(1)a|b)", "cbb" },
- { MU, A, 0, 0 | F_DIFF, "(?(?=(a))(aaaa|a?))+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?=a)(aaaa|a?))+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?!(b))(aaaa|a?))+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?!b)(aaaa|a?))+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0 | F_DIFF, "(?(?=(a))a*)+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?=a)a*)+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?!(b))a*)+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?!b)a*)+aak", "aaaaab aaaaak" },
- { MU, A, 0, 0, "(?(?=(?=(?!(x))a)aa)aaa|(?(?=(?!y)bb)bbb))*k", "abaabbaaabbbaaabbb abaabbaaabbbaaabbbk" },
- { MU, A, 0, 0, "(?P<Name>a)?(?P<Name2>b)?(?(Name)c|d)*l", "bc ddd abccabccl" },
- { MU, A, 0, 0, "(?P<Name>a)?(?P<Name2>b)?(?(Name)c|d)+?dd", "bcabcacdb bdddd" },
- { MU, A, 0, 0, "(?P<Name>a)?(?P<Name2>b)?(?(Name)c|d)+l", "ababccddabdbccd abcccl" },
- { MU, A, 0, 0, "((?:a|aa)(?(1)aaa))x", "aax" },
- { MU, A, 0, 0, "(?(?!)a|b)", "ab" },
- { MU, A, 0, 0, "(?(?!)a)", "ab" },
- { MU, A, 0, 0 | F_NOMATCH, "(?(?!)a|b)", "ac" },
-
- /* Set start of match. */
- { MU, A, 0, 0, "(?:\\Ka)*aaaab", "aaaaaaaa aaaaaaabb" },
- { MU, A, 0, 0, "(?>\\Ka\\Ka)*aaaab", "aaaaaaaa aaaaaaaaaabb" },
- { MU, A, 0, 0, "a+\\K(?<=\\Gaa)a", "aaaaaa" },
- { MU, A, PCRE2_NOTEMPTY, 0 | F_NOMATCH, "a\\K(*ACCEPT)b", "aa" },
- { MU, A, PCRE2_NOTEMPTY_ATSTART, 0, "a\\K(*ACCEPT)b", "aa" },
-
- /* First line. */
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_PROPERTY, "\\p{Any}a", "bb\naaa" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH | F_PROPERTY, "\\p{Any}a", "bb\r\naaa" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0, "(?<=a)", "a" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH, "[^a][^b]", "ab" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH, "a", "\na" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH, "[abc]", "\na" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH, "^a", "\na" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0 | F_NOMATCH, "^(?<=\n)", "\na" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0, "\xf0\x90\x90\x80", "\xf0\x90\x90\x80" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_ANY, 0, 0 | F_NOMATCH, "#", "\xc2\x85#" },
- { M | PCRE2_FIRSTLINE, PCRE2_NEWLINE_ANY, 0, 0 | F_NOMATCH, "#", "\x85#" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_ANY, 0, 0 | F_NOMATCH, "^#", "\xe2\x80\xa8#" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 0 | F_PROPERTY, "\\p{Any}", "\r\na" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 0, ".", "\r" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 0, "a", "\ra" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 0 | F_NOMATCH, "ba", "bbb\r\nba" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 0 | F_NOMATCH | F_PROPERTY, "\\p{Any}{4}|a", "\r\na" },
- { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 1, ".", "\r\n" },
- { PCRE2_FIRSTLINE | PCRE2_DOTALL, PCRE2_NEWLINE_LF, 0, 0 | F_NOMATCH, "ab.", "ab" },
- { MU | PCRE2_FIRSTLINE, A, 0, 1 | F_NOMATCH, "^[a-d0-9]", "\nxx\nd" },
- { PCRE2_FIRSTLINE | PCRE2_DOTALL, PCRE2_NEWLINE_ANY, 0, 0, "....a", "012\n0a" },
- { MU | PCRE2_FIRSTLINE, A, 0, 0, "[aC]", "a" },
-
- /* Recurse. */
- { MU, A, 0, 0, "(a)(?1)", "aa" },
- { MU, A, 0, 0, "((a))(?1)", "aa" },
- { MU, A, 0, 0, "(b|a)(?1)", "aa" },
- { MU, A, 0, 0, "(b|(a))(?1)", "aa" },
- { MU, A, 0, 0 | F_NOMATCH, "((a)(b)(?:a*))(?1)", "aba" },
- { MU, A, 0, 0, "((a)(b)(?:a*))(?1)", "abab" },
- { MU, A, 0, 0, "((a+)c(?2))b(?1)", "aacaabaca" },
- { MU, A, 0, 0, "((?2)b|(a)){2}(?1)", "aabab" },
- { MU, A, 0, 0, "(?1)(a)*+(?2)(b(?1))", "aababa" },
- { MU, A, 0, 0, "(?1)(((a(*ACCEPT)))b)", "axaa" },
- { MU, A, 0, 0, "(?1)(?(DEFINE) (((ac(*ACCEPT)))b) )", "akaac" },
- { MU, A, 0, 0, "(a+)b(?1)b\\1", "abaaabaaaaa" },
- { MU, A, 0, 0 | F_NOMATCH, "(?(DEFINE)(aa|a))(?1)ab", "aab" },
- { MU, A, 0, 0, "(?(DEFINE)(a\\Kb))(?1)+ababc", "abababxabababc" },
- { MU, A, 0, 0, "(a\\Kb)(?1)+ababc", "abababxababababc" },
- { MU, A, 0, 0 | F_NOMATCH, "(a\\Kb)(?1)+ababc", "abababxababababxc" },
- { MU, A, 0, 0, "b|<(?R)*>", "<<b>" },
- { MU, A, 0, 0, "(a\\K){0}(?:(?1)b|ac)", "ac" },
- { MU, A, 0, 0, "(?(DEFINE)(a(?2)|b)(b(?1)|(a)))(?:(?1)|(?2))m", "ababababnababababaam" },
- { MU, A, 0, 0, "(a)((?(R)a|b))(?2)", "aabbabaa" },
- { MU, A, 0, 0, "(a)((?(R2)a|b))(?2)", "aabbabaa" },
- { MU, A, 0, 0, "(a)((?(R1)a|b))(?2)", "ababba" },
- { MU, A, 0, 0, "(?(R0)aa|bb(?R))", "abba aabb bbaa" },
- { MU, A, 0, 0, "((?(R)(?:aaaa|a)|(?:(aaaa)|(a)))+)(?1)$", "aaaaaaaaaa aaaa" },
- { MU, A, 0, 0, "(?P<Name>a(?(R&Name)a|b))(?1)", "aab abb abaa" },
- { MU, A, 0, 0, "((?(R)a|(?1)){3})", "XaaaaaaaaaX" },
- { MU, A, 0, 0, "((?:(?(R)a|(?1))){3})", "XaaaaaaaaaX" },
- { MU, A, 0, 0, "((?(R)a|(?1)){1,3})aaaaaa", "aaaaaaaaXaaaaaaaaa" },
- { MU, A, 0, 0, "((?(R)a|(?1)){1,3}?)M", "aaaM" },
-
- /* 16 bit specific tests. */
- { CM, A, 0, 0 | F_FORCECONV, "\xc3\xa1", "\xc3\x81\xc3\xa1" },
- { CM, A, 0, 0 | F_FORCECONV, "\xe1\xbd\xb8", "\xe1\xbf\xb8\xe1\xbd\xb8" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xc3\xa1]", "\xc3\x81\xc3\xa1" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xe1\xbd\xb8]", "\xe1\xbf\xb8\xe1\xbd\xb8" },
- { CM, A, 0, 0 | F_FORCECONV, "[a-\xed\xb0\x80]", "A" },
- { CM, A, 0, 0 | F_NO8 | F_FORCECONV, "[a-\\x{dc00}]", "B" },
- { CM, A, 0, 0 | F_NO8 | F_NOMATCH | F_FORCECONV, "[b-\\x{dc00}]", "a" },
- { CM, A, 0, 0 | F_NO8 | F_FORCECONV, "\xed\xa0\x80\\x{d800}\xed\xb0\x80\\x{dc00}", "\xed\xa0\x80\xed\xa0\x80\xed\xb0\x80\xed\xb0\x80" },
- { CM, A, 0, 0 | F_NO8 | F_FORCECONV, "[\xed\xa0\x80\\x{d800}]{1,2}?[\xed\xb0\x80\\x{dc00}]{1,2}?#", "\xed\xa0\x80\xed\xa0\x80\xed\xb0\x80\xed\xb0\x80#" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xed\xa0\x80\xed\xb0\x80#]{0,3}(?<=\xed\xb0\x80.)", "\xed\xa0\x80#\xed\xa0\x80##\xed\xb0\x80\xed\xa0\x80" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xed\xa0\x80-\xed\xb3\xbf]", "\xed\x9f\xbf\xed\xa0\x83" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xed\xa0\x80-\xed\xb3\xbf]", "\xed\xb4\x80\xed\xb3\xb0" },
- { CM, A, 0, 0 | F_NO8 | F_FORCECONV, "[\\x{d800}-\\x{dcff}]", "\xed\x9f\xbf\xed\xa0\x83" },
- { CM, A, 0, 0 | F_NO8 | F_FORCECONV, "[\\x{d800}-\\x{dcff}]", "\xed\xb4\x80\xed\xb3\xb0" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xed\xa0\x80-\xef\xbf\xbf]+[\x1-\xed\xb0\x80]+#", "\xed\xa0\x85\xc3\x81\xed\xa0\x85\xef\xbf\xb0\xc2\x85\xed\xa9\x89#" },
- { CM, A, 0, 0 | F_FORCECONV, "[\xed\xa0\x80][\xed\xb0\x80]{2,}", "\xed\xa0\x80\xed\xb0\x80\xed\xa0\x80\xed\xb0\x80\xed\xb0\x80\xed\xb0\x80" },
- { M, A, 0, 0 | F_FORCECONV, "[^\xed\xb0\x80]{3,}?", "##\xed\xb0\x80#\xed\xb0\x80#\xc3\x89#\xed\xb0\x80" },
- { M, A, 0, 0 | F_NO8 | F_FORCECONV, "[^\\x{dc00}]{3,}?", "##\xed\xb0\x80#\xed\xb0\x80#\xc3\x89#\xed\xb0\x80" },
- { CM, A, 0, 0 | F_FORCECONV, ".\\B.", "\xed\xa0\x80\xed\xb0\x80" },
- { CM, A, 0, 0 | F_FORCECONV, "\\D+(?:\\d+|.)\\S+(?:\\s+|.)\\W+(?:\\w+|.)\xed\xa0\x80\xed\xa0\x80", "\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80" },
- { CM, A, 0, 0 | F_FORCECONV, "\\d*\\s*\\w*\xed\xa0\x80\xed\xa0\x80", "\xed\xa0\x80\xed\xa0\x80" },
- { CM, A, 0, 0 | F_FORCECONV | F_NOMATCH, "\\d*?\\D*?\\s*?\\S*?\\w*?\\W*?##", "\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80\xed\xa0\x80#" },
- { CM | PCRE2_EXTENDED, A, 0, 0 | F_FORCECONV, "\xed\xa0\x80 \xed\xb0\x80 !", "\xed\xa0\x80\xed\xb0\x80!" },
- { CM, A, 0, 0 | F_FORCECONV, "\xed\xa0\x80+#[^#]+\xed\xa0\x80", "\xed\xa0\x80#a\xed\xa0\x80" },
- { CM, A, 0, 0 | F_FORCECONV, "(\xed\xa0\x80+)#\\1", "\xed\xa0\x80\xed\xa0\x80#\xed\xa0\x80\xed\xa0\x80" },
- { M, PCRE2_NEWLINE_ANY, 0, 0 | F_NO8 | F_FORCECONV, "^-", "a--\xe2\x80\xa8--" },
- { 0, BSR(PCRE2_BSR_UNICODE), 0, 0 | F_NO8 | F_FORCECONV, "\\R", "ab\xe2\x80\xa8" },
- { 0, 0, 0, 0 | F_NO8 | F_FORCECONV, "\\v", "ab\xe2\x80\xa9" },
- { 0, 0, 0, 0 | F_NO8 | F_FORCECONV, "\\h", "ab\xe1\xa0\x8e" },
- { 0, 0, 0, 0 | F_NO8 | F_FORCECONV, "\\v+?\\V+?#", "\xe2\x80\xa9\xe2\x80\xa9\xef\xbf\xbf\xef\xbf\xbf#" },
- { 0, 0, 0, 0 | F_NO8 | F_FORCECONV, "\\h+?\\H+?#", "\xe1\xa0\x8e\xe1\xa0\x8e\xef\xbf\xbf\xef\xbf\xbf#" },
-
- /* Partial matching. */
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "ab", "a" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "ab|a", "a" },
- { MU, A, PCRE2_PARTIAL_HARD, 0, "ab|a", "a" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "\\b#", "a" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "(?<=a)b", "a" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "abc|(?<=xxa)bc", "xxab" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "a\\B", "a" },
- { MU, A, PCRE2_PARTIAL_HARD, 0, "a\\b", "a" },
-
- /* (*MARK) verb. */
- { MU, A, 0, 0, "a(*MARK:aa)a", "ababaa" },
- { MU, A, 0, 0 | F_NOMATCH, "a(*:aa)a", "abab" },
- { MU, A, 0, 0, "a(*:aa)(b(*:bb)b|bc)", "abc" },
- { MU, A, 0, 0 | F_NOMATCH, "a(*:1)x|b(*:2)y", "abc" },
- { MU, A, 0, 0, "(?>a(*:aa))b|ac", "ac" },
- { MU, A, 0, 0, "(?(DEFINE)(a(*:aa)))(?1)", "a" },
- { MU, A, 0, 0 | F_NOMATCH, "(?(DEFINE)((a)(*:aa)))(?1)b", "aa" },
- { MU, A, 0, 0, "(?(DEFINE)(a(*:aa)))a(?1)b|aac", "aac" },
- { MU, A, 0, 0, "(a(*:aa)){0}(?:b(?1)b|c)+c", "babbab cc" },
- { MU, A, 0, 0, "(a(*:aa)){0}(?:b(?1)b)+", "babba" },
- { MU, A, 0, 0 | F_NOMATCH, "(a(*:aa)){0}(?:b(?1)b)+", "ba" },
- { MU, A, 0, 0, "(a\\K(*:aa)){0}(?:b(?1)b|c)+c", "babbab cc" },
- { MU, A, 0, 0, "(a\\K(*:aa)){0}(?:b(?1)b)+", "babba" },
- { MU, A, 0, 0 | F_NOMATCH, "(a\\K(*:aa)){0}(?:b(?1)b)+", "ba" },
- { MU, A, 0, 0 | F_NOMATCH, "(*:mark)m", "a" },
-
- /* (*COMMIT) verb. */
- { MU, A, 0, 0 | F_NOMATCH, "a(*COMMIT)b", "ac" },
- { MU, A, 0, 0, "aa(*COMMIT)b", "xaxaab" },
- { MU, A, 0, 0 | F_NOMATCH, "a(*COMMIT)(*:msg)b|ac", "ac" },
- { MU, A, 0, 0 | F_NOMATCH, "(a(*COMMIT)b)++", "abac" },
- { MU, A, 0, 0 | F_NOMATCH, "((a)(*COMMIT)b)++", "abac" },
- { MU, A, 0, 0 | F_NOMATCH, "(?=a(*COMMIT)b)ab|ad", "ad" },
-
- /* (*PRUNE) verb. */
- { MU, A, 0, 0, "aa\\K(*PRUNE)b", "aaab" },
- { MU, A, 0, 0, "aa(*PRUNE:bb)b|a", "aa" },
- { MU, A, 0, 0, "(a)(a)(*PRUNE)b|(a)", "aa" },
- { MU, A, 0, 0, "(a)(a)(a)(a)(a)(a)(a)(a)(*PRUNE)b|(a)", "aaaaaaaa" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "a(*PRUNE)a|", "a" },
- { MU, A, PCRE2_PARTIAL_SOFT, 0, "a(*PRUNE)a|m", "a" },
- { MU, A, 0, 0 | F_NOMATCH, "(?=a(*PRUNE)b)ab|ad", "ad" },
- { MU, A, 0, 0, "a(*COMMIT)(*PRUNE)d|bc", "abc" },
- { MU, A, 0, 0, "(?=a(*COMMIT)b)a(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?=a(*COMMIT)b)a(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0, "(?=(a)(*COMMIT)b)a(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?=(a)(*COMMIT)b)a(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0, "(a(*COMMIT)b){0}a(?1)(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0 | F_NOMATCH, "(a(*COMMIT)b){0}a(*COMMIT)(?1)(*PRUNE)c|bc", "abc" },
- { MU, A, 0, 0, "(a(*COMMIT)b)++(*PRUNE)d|c", "ababc" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(a(*COMMIT)b)++(*PRUNE)d|c", "ababc" },
- { MU, A, 0, 0, "((a)(*COMMIT)b)++(*PRUNE)d|c", "ababc" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)((a)(*COMMIT)b)++(*PRUNE)d|c", "ababc" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)*abab(*PRUNE)d|ba", "ababab" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)*abab(*PRUNE)d|ba", "ababab" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)+abab(*PRUNE)d|ba", "ababab" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)+abab(*PRUNE)d|ba", "ababab" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)?ab(*PRUNE)d|ba", "aba" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)?ab(*PRUNE)d|ba", "aba" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)*?n(*PRUNE)d|ba", "abababn" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)*?n(*PRUNE)d|ba", "abababn" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)+?n(*PRUNE)d|ba", "abababn" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)+?n(*PRUNE)d|ba", "abababn" },
- { MU, A, 0, 0, "(?>a(*COMMIT)b)??n(*PRUNE)d|bn", "abn" },
- { MU, A, 0, 0 | F_NOMATCH, "(*COMMIT)(?>a(*COMMIT)b)??n(*PRUNE)d|bn", "abn" },
-
- /* (*SKIP) verb. */
- { MU, A, 0, 0 | F_NOMATCH, "(?=a(*SKIP)b)ab|ad", "ad" },
- { MU, A, 0, 0, "(\\w+(*SKIP)#)", "abcd,xyz#," },
- { MU, A, 0, 0, "\\w+(*SKIP)#|mm", "abcd,xyz#," },
- { MU, A, 0, 0 | F_NOMATCH, "b+(?<=(*SKIP)#c)|b+", "#bbb" },
-
- /* (*THEN) verb. */
- { MU, A, 0, 0, "((?:a(*THEN)|aab)(*THEN)c|a+)+m", "aabcaabcaabcaabcnacm" },
- { MU, A, 0, 0 | F_NOMATCH, "((?:a(*THEN)|aab)(*THEN)c|a+)+m", "aabcm" },
- { MU, A, 0, 0, "((?:a(*THEN)|aab)c|a+)+m", "aabcaabcnmaabcaabcm" },
- { MU, A, 0, 0, "((?:a|aab)(*THEN)c|a+)+m", "aam" },
- { MU, A, 0, 0, "((?:a(*COMMIT)|aab)(*THEN)c|a+)+m", "aam" },
- { MU, A, 0, 0, "(?(?=a(*THEN)b)ab|ad)", "ad" },
- { MU, A, 0, 0, "(?(?!a(*THEN)b)ad|add)", "add" },
- { MU, A, 0, 0 | F_NOMATCH, "(?(?=a)a(*THEN)b|ad)", "ad" },
- { MU, A, 0, 0, "(?!(?(?=a)ab|b(*THEN)d))bn|bnn", "bnn" },
-
- /* Deep recursion. */
- { MU, A, 0, 0, "((((?:(?:(?:\\w)+)?)*|(?>\\w)+?)+|(?>\\w)?\?)*)?\\s", "aaaaa+ " },
- { MU, A, 0, 0, "(?:((?:(?:(?:\\w*?)+)??|(?>\\w)?|\\w*+)*)+)+?\\s", "aa+ " },
- { MU, A, 0, 0, "((a?)+)+b", "aaaaaaaaaaaa b" },
-
- /* Deep recursion: Stack limit reached. */
- { M, A, 0, 0 | F_NOMATCH, "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaa" },
- { M, A, 0, 0 | F_NOMATCH, "(?:a+)+b", "aaaaaaaaaaaaaaaaaaaaaaaa b" },
- { M, A, 0, 0 | F_NOMATCH, "(?:a+?)+?b", "aaaaaaaaaaaaaaaaaaaaaaaa b" },
- { M, A, 0, 0 | F_NOMATCH, "(?:a*)*b", "aaaaaaaaaaaaaaaaaaaaaaaa b" },
- { M, A, 0, 0 | F_NOMATCH, "(?:a*?)*?b", "aaaaaaaaaaaaaaaaaaaaaaaa b" },
-
- { 0, 0, 0, 0, NULL, NULL }
-};
-
-#ifdef SUPPORT_PCRE2_8
-static pcre2_jit_stack_8* callback8(void *arg)
-{
- return (pcre2_jit_stack_8 *)arg;
-}
-#endif
-
-#ifdef SUPPORT_PCRE2_16
-static pcre2_jit_stack_16* callback16(void *arg)
-{
- return (pcre2_jit_stack_16 *)arg;
-}
-#endif
-
-#ifdef SUPPORT_PCRE2_32
-static pcre2_jit_stack_32* callback32(void *arg)
-{
- return (pcre2_jit_stack_32 *)arg;
-}
-#endif
-
-#ifdef SUPPORT_PCRE2_8
-static pcre2_jit_stack_8 *stack8;
-
-static pcre2_jit_stack_8 *getstack8(void)
-{
- if (!stack8)
- stack8 = pcre2_jit_stack_create_8(1, 1024 * 1024, NULL);
- return stack8;
-}
-
-static void setstack8(pcre2_match_context_8 *mcontext)
-{
- if (!mcontext) {
- if (stack8)
- pcre2_jit_stack_free_8(stack8);
- stack8 = NULL;
- return;
- }
-
- pcre2_jit_stack_assign_8(mcontext, callback8, getstack8());
-}
-#endif /* SUPPORT_PCRE2_8 */
-
-#ifdef SUPPORT_PCRE2_16
-static pcre2_jit_stack_16 *stack16;
-
-static pcre2_jit_stack_16 *getstack16(void)
-{
- if (!stack16)
- stack16 = pcre2_jit_stack_create_16(1, 1024 * 1024, NULL);
- return stack16;
-}
-
-static void setstack16(pcre2_match_context_16 *mcontext)
-{
- if (!mcontext) {
- if (stack16)
- pcre2_jit_stack_free_16(stack16);
- stack16 = NULL;
- return;
- }
-
- pcre2_jit_stack_assign_16(mcontext, callback16, getstack16());
-}
-#endif /* SUPPORT_PCRE2_16 */
-
-#ifdef SUPPORT_PCRE2_32
-static pcre2_jit_stack_32 *stack32;
-
-static pcre2_jit_stack_32 *getstack32(void)
-{
- if (!stack32)
- stack32 = pcre2_jit_stack_create_32(1, 1024 * 1024, NULL);
- return stack32;
-}
-
-static void setstack32(pcre2_match_context_32 *mcontext)
-{
- if (!mcontext) {
- if (stack32)
- pcre2_jit_stack_free_32(stack32);
- stack32 = NULL;
- return;
- }
-
- pcre2_jit_stack_assign_32(mcontext, callback32, getstack32());
-}
-#endif /* SUPPORT_PCRE2_32 */
-
-#ifdef SUPPORT_PCRE2_16
-
-static int convert_utf8_to_utf16(PCRE2_SPTR8 input, PCRE2_UCHAR16 *output, int *offsetmap, int max_length)
-{
- PCRE2_SPTR8 iptr = input;
- PCRE2_UCHAR16 *optr = output;
- unsigned int c;
-
- if (max_length == 0)
- return 0;
-
- while (*iptr && max_length > 1) {
- c = 0;
- if (offsetmap)
- *offsetmap++ = (int)(iptr - (unsigned char*)input);
-
- if (*iptr < 0xc0)
- c = *iptr++;
- else if (!(*iptr & 0x20)) {
- c = ((iptr[0] & 0x1f) << 6) | (iptr[1] & 0x3f);
- iptr += 2;
- } else if (!(*iptr & 0x10)) {
- c = ((iptr[0] & 0x0f) << 12) | ((iptr[1] & 0x3f) << 6) | (iptr[2] & 0x3f);
- iptr += 3;
- } else if (!(*iptr & 0x08)) {
- c = ((iptr[0] & 0x07) << 18) | ((iptr[1] & 0x3f) << 12) | ((iptr[2] & 0x3f) << 6) | (iptr[3] & 0x3f);
- iptr += 4;
- }
-
- if (c < 65536) {
- *optr++ = c;
- max_length--;
- } else if (max_length <= 2) {
- *optr = '\0';
- return (int)(optr - output);
- } else {
- c -= 0x10000;
- *optr++ = 0xd800 | ((c >> 10) & 0x3ff);
- *optr++ = 0xdc00 | (c & 0x3ff);
- max_length -= 2;
- if (offsetmap)
- offsetmap++;
- }
- }
- if (offsetmap)
- *offsetmap = (int)(iptr - (unsigned char*)input);
- *optr = '\0';
- return (int)(optr - output);
-}
-
-static int copy_char8_to_char16(PCRE2_SPTR8 input, PCRE2_UCHAR16 *output, int max_length)
-{
- PCRE2_SPTR8 iptr = input;
- PCRE2_UCHAR16 *optr = output;
-
- if (max_length == 0)
- return 0;
-
- while (*iptr && max_length > 1) {
- *optr++ = *iptr++;
- max_length--;
- }
- *optr = '\0';
- return (int)(optr - output);
-}
-
-#define REGTEST_MAX_LENGTH16 4096
-static PCRE2_UCHAR16 regtest_buf16[REGTEST_MAX_LENGTH16];
-static int regtest_offsetmap16[REGTEST_MAX_LENGTH16];
-
-#endif /* SUPPORT_PCRE2_16 */
-
-#ifdef SUPPORT_PCRE2_32
-
-static int convert_utf8_to_utf32(PCRE2_SPTR8 input, PCRE2_UCHAR32 *output, int *offsetmap, int max_length)
-{
- PCRE2_SPTR8 iptr = input;
- PCRE2_UCHAR32 *optr = output;
- unsigned int c;
-
- if (max_length == 0)
- return 0;
-
- while (*iptr && max_length > 1) {
- c = 0;
- if (offsetmap)
- *offsetmap++ = (int)(iptr - (unsigned char*)input);
-
- if (*iptr < 0xc0)
- c = *iptr++;
- else if (!(*iptr & 0x20)) {
- c = ((iptr[0] & 0x1f) << 6) | (iptr[1] & 0x3f);
- iptr += 2;
- } else if (!(*iptr & 0x10)) {
- c = ((iptr[0] & 0x0f) << 12) | ((iptr[1] & 0x3f) << 6) | (iptr[2] & 0x3f);
- iptr += 3;
- } else if (!(*iptr & 0x08)) {
- c = ((iptr[0] & 0x07) << 18) | ((iptr[1] & 0x3f) << 12) | ((iptr[2] & 0x3f) << 6) | (iptr[3] & 0x3f);
- iptr += 4;
- }
-
- *optr++ = c;
- max_length--;
- }
- if (offsetmap)
- *offsetmap = (int)(iptr - (unsigned char*)input);
- *optr = 0;
- return (int)(optr - output);
-}
-
-static int copy_char8_to_char32(PCRE2_SPTR8 input, PCRE2_UCHAR32 *output, int max_length)
-{
- PCRE2_SPTR8 iptr = input;
- PCRE2_UCHAR32 *optr = output;
-
- if (max_length == 0)
- return 0;
-
- while (*iptr && max_length > 1) {
- *optr++ = *iptr++;
- max_length--;
- }
- *optr = '\0';
- return (int)(optr - output);
-}
-
-#define REGTEST_MAX_LENGTH32 4096
-static PCRE2_UCHAR32 regtest_buf32[REGTEST_MAX_LENGTH32];
-static int regtest_offsetmap32[REGTEST_MAX_LENGTH32];
-
-#endif /* SUPPORT_PCRE2_32 */
-
-static int check_ascii(const char *input)
-{
- const unsigned char *ptr = (unsigned char *)input;
- while (*ptr) {
- if (*ptr > 127)
- return 0;
- ptr++;
- }
- return 1;
-}
-
-#define OVECTOR_SIZE 15
-
-static int regression_tests(void)
-{
- struct regression_test_case *current = regression_test_cases;
- int error;
- PCRE2_SIZE err_offs;
- int is_successful;
- int is_ascii;
- int total = 0;
- int successful = 0;
- int successful_row = 0;
- int counter = 0;
- int jit_compile_mode;
- int utf = 0;
- int disabled_options = 0;
- int i;
-#ifdef SUPPORT_PCRE2_8
- pcre2_code_8 *re8;
- pcre2_compile_context_8 *ccontext8;
- pcre2_match_data_8 *mdata8_1;
- pcre2_match_data_8 *mdata8_2;
- pcre2_match_context_8 *mcontext8;
- PCRE2_SIZE *ovector8_1 = NULL;
- PCRE2_SIZE *ovector8_2 = NULL;
- int return_value8[2];
-#endif
-#ifdef SUPPORT_PCRE2_16
- pcre2_code_16 *re16;
- pcre2_compile_context_16 *ccontext16;
- pcre2_match_data_16 *mdata16_1;
- pcre2_match_data_16 *mdata16_2;
- pcre2_match_context_16 *mcontext16;
- PCRE2_SIZE *ovector16_1 = NULL;
- PCRE2_SIZE *ovector16_2 = NULL;
- int return_value16[2];
- int length16;
-#endif
-#ifdef SUPPORT_PCRE2_32
- pcre2_code_32 *re32;
- pcre2_compile_context_32 *ccontext32;
- pcre2_match_data_32 *mdata32_1;
- pcre2_match_data_32 *mdata32_2;
- pcre2_match_context_32 *mcontext32;
- PCRE2_SIZE *ovector32_1 = NULL;
- PCRE2_SIZE *ovector32_2 = NULL;
- int return_value32[2];
- int length32;
-#endif
-
-#if defined SUPPORT_PCRE2_8
- PCRE2_UCHAR8 cpu_info[128];
-#elif defined SUPPORT_PCRE2_16
- PCRE2_UCHAR16 cpu_info[128];
-#elif defined SUPPORT_PCRE2_32
- PCRE2_UCHAR32 cpu_info[128];
-#endif
-#if defined SUPPORT_UTF && ((defined(SUPPORT_PCRE2_8) + defined(SUPPORT_PCRE2_16) + defined(SUPPORT_PCRE2_32)) >= 2)
- int return_value;
-#endif
-
- /* This test compares the behaviour of interpreter and JIT. Although disabling
- utf or ucp may make tests fail, if the pcre_exec result is the SAME, it is
- still considered successful from pcre_jit_test point of view. */
-
-#if defined SUPPORT_PCRE2_8
- pcre2_config_8(PCRE2_CONFIG_JITTARGET, &cpu_info);
-#elif defined SUPPORT_PCRE2_16
- pcre2_config_16(PCRE2_CONFIG_JITTARGET, &cpu_info);
-#elif defined SUPPORT_PCRE2_32
- pcre2_config_32(PCRE2_CONFIG_JITTARGET, &cpu_info);
-#endif
-
- printf("Running JIT regression tests\n");
- printf(" target CPU of SLJIT compiler: ");
- for (i = 0; cpu_info[i]; i++)
- printf("%c", (char)(cpu_info[i]));
- printf("\n");
-
-#if defined SUPPORT_PCRE2_8
- pcre2_config_8(PCRE2_CONFIG_UNICODE, &utf);
-#elif defined SUPPORT_PCRE2_16
- pcre2_config_16(PCRE2_CONFIG_UNICODE, &utf);
-#elif defined SUPPORT_PCRE2_32
- pcre2_config_32(PCRE2_CONFIG_UNICODE, &utf);
-#endif
-
- if (!utf)
- disabled_options |= PCRE2_UTF;
-#ifdef SUPPORT_PCRE2_8
- printf(" in 8 bit mode with UTF-8 %s:\n", utf ? "enabled" : "disabled");
-#endif
-#ifdef SUPPORT_PCRE2_16
- printf(" in 16 bit mode with UTF-16 %s:\n", utf ? "enabled" : "disabled");
-#endif
-#ifdef SUPPORT_PCRE2_32
- printf(" in 32 bit mode with UTF-32 %s:\n", utf ? "enabled" : "disabled");
-#endif
-
- while (current->pattern) {
- /* printf("\nPattern: %s :\n", current->pattern); */
- total++;
- is_ascii = 0;
- if (!(current->start_offset & F_PROPERTY))
- is_ascii = check_ascii(current->pattern) && check_ascii(current->input);
-
- if (current->match_options & PCRE2_PARTIAL_SOFT)
- jit_compile_mode = PCRE2_JIT_PARTIAL_SOFT;
- else if (current->match_options & PCRE2_PARTIAL_HARD)
- jit_compile_mode = PCRE2_JIT_PARTIAL_HARD;
- else
- jit_compile_mode = PCRE2_JIT_COMPLETE;
- error = 0;
-#ifdef SUPPORT_PCRE2_8
- re8 = NULL;
- ccontext8 = pcre2_compile_context_create_8(NULL);
- if (ccontext8) {
- if (GET_NEWLINE(current->newline))
- pcre2_set_newline_8(ccontext8, GET_NEWLINE(current->newline));
- if (GET_BSR(current->newline))
- pcre2_set_bsr_8(ccontext8, GET_BSR(current->newline));
-
- if (!(current->start_offset & F_NO8)) {
- re8 = pcre2_compile_8((PCRE2_SPTR8)current->pattern, PCRE2_ZERO_TERMINATED,
- current->compile_options & ~disabled_options,
- &error, &err_offs, ccontext8);
-
- if (!re8 && (utf || is_ascii))
- printf("\n8 bit: Cannot compile pattern \"%s\": %d\n", current->pattern, error);
- }
- pcre2_compile_context_free_8(ccontext8);
- }
- else
- printf("\n8 bit: Cannot allocate compile context\n");
-#endif
-#ifdef SUPPORT_PCRE2_16
- if ((current->compile_options & PCRE2_UTF) || (current->start_offset & F_FORCECONV))
- convert_utf8_to_utf16((PCRE2_SPTR8)current->pattern, regtest_buf16, NULL, REGTEST_MAX_LENGTH16);
- else
- copy_char8_to_char16((PCRE2_SPTR8)current->pattern, regtest_buf16, REGTEST_MAX_LENGTH16);
-
- re16 = NULL;
- ccontext16 = pcre2_compile_context_create_16(NULL);
- if (ccontext16) {
- if (GET_NEWLINE(current->newline))
- pcre2_set_newline_16(ccontext16, GET_NEWLINE(current->newline));
- if (GET_BSR(current->newline))
- pcre2_set_bsr_16(ccontext16, GET_BSR(current->newline));
-
- if (!(current->start_offset & F_NO16)) {
- re16 = pcre2_compile_16(regtest_buf16, PCRE2_ZERO_TERMINATED,
- current->compile_options & ~disabled_options,
- &error, &err_offs, ccontext16);
-
- if (!re16 && (utf || is_ascii))
- printf("\n16 bit: Cannot compile pattern \"%s\": %d\n", current->pattern, error);
- }
- pcre2_compile_context_free_16(ccontext16);
- }
- else
- printf("\n16 bit: Cannot allocate compile context\n");
-#endif
-#ifdef SUPPORT_PCRE2_32
- if ((current->compile_options & PCRE2_UTF) || (current->start_offset & F_FORCECONV))
- convert_utf8_to_utf32((PCRE2_SPTR8)current->pattern, regtest_buf32, NULL, REGTEST_MAX_LENGTH32);
- else
- copy_char8_to_char32((PCRE2_SPTR8)current->pattern, regtest_buf32, REGTEST_MAX_LENGTH32);
-
- re32 = NULL;
- ccontext32 = pcre2_compile_context_create_32(NULL);
- if (ccontext32) {
- if (GET_NEWLINE(current->newline))
- pcre2_set_newline_32(ccontext32, GET_NEWLINE(current->newline));
- if (GET_BSR(current->newline))
- pcre2_set_bsr_32(ccontext32, GET_BSR(current->newline));
-
- if (!(current->start_offset & F_NO32)) {
- re32 = pcre2_compile_32(regtest_buf32, PCRE2_ZERO_TERMINATED,
- current->compile_options & ~disabled_options,
- &error, &err_offs, ccontext32);
-
- if (!re32 && (utf || is_ascii))
- printf("\n32 bit: Cannot compile pattern \"%s\": %d\n", current->pattern, error);
- }
- pcre2_compile_context_free_32(ccontext32);
- }
- else
- printf("\n32 bit: Cannot allocate compile context\n");
-#endif
-
- counter++;
- if ((counter & 0x3) != 0) {
-#ifdef SUPPORT_PCRE2_8
- setstack8(NULL);
-#endif
-#ifdef SUPPORT_PCRE2_16
- setstack16(NULL);
-#endif
-#ifdef SUPPORT_PCRE2_32
- setstack32(NULL);
-#endif
- }
-
-#ifdef SUPPORT_PCRE2_8
- return_value8[0] = -1000;
- return_value8[1] = -1000;
- mdata8_1 = pcre2_match_data_create_8(OVECTOR_SIZE, NULL);
- mdata8_2 = pcre2_match_data_create_8(OVECTOR_SIZE, NULL);
- mcontext8 = pcre2_match_context_create_8(NULL);
- if (!mdata8_1 || !mdata8_2 || !mcontext8) {
- printf("\n8 bit: Cannot allocate match data\n");
- pcre2_match_data_free_8(mdata8_1);
- pcre2_match_data_free_8(mdata8_2);
- pcre2_match_context_free_8(mcontext8);
- pcre2_code_free_8(re8);
- re8 = NULL;
- } else {
- ovector8_1 = pcre2_get_ovector_pointer_8(mdata8_1);
- ovector8_2 = pcre2_get_ovector_pointer_8(mdata8_2);
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector8_1[i] = -2;
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector8_2[i] = -2;
- }
- if (re8) {
- return_value8[1] = pcre2_match_8(re8, (PCRE2_SPTR8)current->input, strlen(current->input),
- current->start_offset & OFFSET_MASK, current->match_options, mdata8_2, NULL);
-
- if (pcre2_jit_compile_8(re8, jit_compile_mode)) {
- printf("\n8 bit: JIT compiler does not support \"%s\"\n", current->pattern);
- } else if ((counter & 0x1) != 0) {
- setstack8(mcontext8);
- return_value8[0] = pcre2_match_8(re8, (PCRE2_SPTR8)current->input, strlen(current->input),
- current->start_offset & OFFSET_MASK, current->match_options, mdata8_1, mcontext8);
- } else {
- pcre2_jit_stack_assign_8(mcontext8, NULL, getstack8());
- return_value8[0] = pcre2_jit_match_8(re8, (PCRE2_SPTR8)current->input, strlen(current->input),
- current->start_offset & OFFSET_MASK, current->match_options, mdata8_1, mcontext8);
- }
- }
-#endif
-
-#ifdef SUPPORT_PCRE2_16
- return_value16[0] = -1000;
- return_value16[1] = -1000;
- mdata16_1 = pcre2_match_data_create_16(OVECTOR_SIZE, NULL);
- mdata16_2 = pcre2_match_data_create_16(OVECTOR_SIZE, NULL);
- mcontext16 = pcre2_match_context_create_16(NULL);
- if (!mdata16_1 || !mdata16_2 || !mcontext16) {
- printf("\n16 bit: Cannot allocate match data\n");
- pcre2_match_data_free_16(mdata16_1);
- pcre2_match_data_free_16(mdata16_2);
- pcre2_match_context_free_16(mcontext16);
- pcre2_code_free_16(re16);
- re16 = NULL;
- } else {
- ovector16_1 = pcre2_get_ovector_pointer_16(mdata16_1);
- ovector16_2 = pcre2_get_ovector_pointer_16(mdata16_2);
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector16_1[i] = -2;
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector16_2[i] = -2;
- }
- if (re16) {
- if ((current->compile_options & PCRE2_UTF) || (current->start_offset & F_FORCECONV))
- length16 = convert_utf8_to_utf16((PCRE2_SPTR8)current->input, regtest_buf16, regtest_offsetmap16, REGTEST_MAX_LENGTH16);
- else
- length16 = copy_char8_to_char16((PCRE2_SPTR8)current->input, regtest_buf16, REGTEST_MAX_LENGTH16);
-
- return_value16[1] = pcre2_match_16(re16, regtest_buf16, length16,
- current->start_offset & OFFSET_MASK, current->match_options, mdata16_2, NULL);
-
- if (pcre2_jit_compile_16(re16, jit_compile_mode)) {
- printf("\n16 bit: JIT compiler does not support \"%s\"\n", current->pattern);
- } else if ((counter & 0x1) != 0) {
- setstack16(mcontext16);
- return_value16[0] = pcre2_match_16(re16, regtest_buf16, length16,
- current->start_offset & OFFSET_MASK, current->match_options, mdata16_1, mcontext16);
- } else {
- pcre2_jit_stack_assign_16(mcontext16, NULL, getstack16());
- return_value16[0] = pcre2_jit_match_16(re16, regtest_buf16, length16,
- current->start_offset & OFFSET_MASK, current->match_options, mdata16_1, mcontext16);
- }
- }
-#endif
-
-#ifdef SUPPORT_PCRE2_32
- return_value32[0] = -1000;
- return_value32[1] = -1000;
- mdata32_1 = pcre2_match_data_create_32(OVECTOR_SIZE, NULL);
- mdata32_2 = pcre2_match_data_create_32(OVECTOR_SIZE, NULL);
- mcontext32 = pcre2_match_context_create_32(NULL);
- if (!mdata32_1 || !mdata32_2 || !mcontext32) {
- printf("\n32 bit: Cannot allocate match data\n");
- pcre2_match_data_free_32(mdata32_1);
- pcre2_match_data_free_32(mdata32_2);
- pcre2_match_context_free_32(mcontext32);
- pcre2_code_free_32(re32);
- re32 = NULL;
- } else {
- ovector32_1 = pcre2_get_ovector_pointer_32(mdata32_1);
- ovector32_2 = pcre2_get_ovector_pointer_32(mdata32_2);
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector32_1[i] = -2;
- for (i = 0; i < OVECTOR_SIZE * 3; ++i)
- ovector32_2[i] = -2;
- }
- if (re32) {
- if ((current->compile_options & PCRE2_UTF) || (current->start_offset & F_FORCECONV))
- length32 = convert_utf8_to_utf32((PCRE2_SPTR8)current->input, regtest_buf32, regtest_offsetmap32, REGTEST_MAX_LENGTH32);
- else
- length32 = copy_char8_to_char32((PCRE2_SPTR8)current->input, regtest_buf32, REGTEST_MAX_LENGTH32);
-
- return_value32[1] = pcre2_match_32(re32, regtest_buf32, length32,
- current->start_offset & OFFSET_MASK, current->match_options, mdata32_2, NULL);
-
- if (pcre2_jit_compile_32(re32, jit_compile_mode)) {
- printf("\n32 bit: JIT compiler does not support \"%s\"\n", current->pattern);
- } else if ((counter & 0x1) != 0) {
- setstack32(mcontext32);
- return_value32[0] = pcre2_match_32(re32, regtest_buf32, length32,
- current->start_offset & OFFSET_MASK, current->match_options, mdata32_1, mcontext32);
- } else {
- pcre2_jit_stack_assign_32(mcontext32, NULL, getstack32());
- return_value32[0] = pcre2_jit_match_32(re32, regtest_buf32, length32,
- current->start_offset & OFFSET_MASK, current->match_options, mdata32_1, mcontext32);
- }
- }
-#endif
-
- /* printf("[%d-%d-%d|%d-%d|%d-%d|%d-%d]%s",
- return_value8[0], return_value16[0], return_value32[0],
- (int)ovector8_1[0], (int)ovector8_1[1],
- (int)ovector16_1[0], (int)ovector16_1[1],
- (int)ovector32_1[0], (int)ovector32_1[1],
- (current->compile_options & PCRE2_CASELESS) ? "C" : ""); */
-
- /* If F_DIFF is set, just run the test, but do not compare the results.
- Segfaults can still be captured. */
-
- is_successful = 1;
- if (!(current->start_offset & F_DIFF)) {
-#if defined SUPPORT_UTF && ((defined(SUPPORT_PCRE2_8) + defined(SUPPORT_PCRE2_16) + defined(SUPPORT_PCRE2_32)) >= 2)
- if (!(current->start_offset & F_FORCECONV)) {
-
- /* All results must be the same. */
-#ifdef SUPPORT_PCRE2_8
- if ((return_value = return_value8[0]) != return_value8[1]) {
- printf("\n8 bit: Return value differs(J8:%d,I8:%d): [%d] '%s' @ '%s'\n",
- return_value8[0], return_value8[1], total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
-#ifdef SUPPORT_PCRE2_16
- if ((return_value = return_value16[0]) != return_value16[1]) {
- printf("\n16 bit: Return value differs(J16:%d,I16:%d): [%d] '%s' @ '%s'\n",
- return_value16[0], return_value16[1], total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
-#ifdef SUPPORT_PCRE2_32
- if ((return_value = return_value32[0]) != return_value32[1]) {
- printf("\n32 bit: Return value differs(J32:%d,I32:%d): [%d] '%s' @ '%s'\n",
- return_value32[0], return_value32[1], total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
-#if defined SUPPORT_PCRE2_8 && defined SUPPORT_PCRE2_16
- if (return_value8[0] != return_value16[0]) {
- printf("\n8 and 16 bit: Return value differs(J8:%d,J16:%d): [%d] '%s' @ '%s'\n",
- return_value8[0], return_value16[0],
- total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
-#if defined SUPPORT_PCRE2_8 && defined SUPPORT_PCRE2_32
- if (return_value8[0] != return_value32[0]) {
- printf("\n8 and 32 bit: Return value differs(J8:%d,J32:%d): [%d] '%s' @ '%s'\n",
- return_value8[0], return_value32[0],
- total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
-#if defined SUPPORT_PCRE2_16 && defined SUPPORT_PCRE2_32
- if (return_value16[0] != return_value32[0]) {
- printf("\n16 and 32 bit: Return value differs(J16:%d,J32:%d): [%d] '%s' @ '%s'\n",
- return_value16[0], return_value32[0],
- total, current->pattern, current->input);
- is_successful = 0;
- } else
-#endif
- if (return_value >= 0 || return_value == PCRE_ERROR_PARTIAL) {
- if (return_value == PCRE_ERROR_PARTIAL) {
- return_value = 2;
- } else {
- return_value *= 2;
- }
-#ifdef SUPPORT_PCRE2_8
- return_value8[0] = return_value;
-#endif
-#ifdef SUPPORT_PCRE2_16
- return_value16[0] = return_value;
-#endif
-#ifdef SUPPORT_PCRE2_32
- return_value32[0] = return_value;
-#endif
- /* Transform back the results. */
- if (current->flags & PCRE_UTF8) {
-#ifdef SUPPORT_PCRE2_16
- for (i = 0; i < return_value; ++i) {
- if (ovector16_1[i] >= 0)
- ovector16_1[i] = regtest_offsetmap16[ovector16_1[i]];
- if (ovector16_2[i] >= 0)
- ovector16_2[i] = regtest_offsetmap16[ovector16_2[i]];
- }
-#endif
-#ifdef SUPPORT_PCRE2_32
- for (i = 0; i < return_value; ++i) {
- if (ovector32_1[i] >= 0)
- ovector32_1[i] = regtest_offsetmap32[ovector32_1[i]];
- if (ovector32_2[i] >= 0)
- ovector32_2[i] = regtest_offsetmap32[ovector32_2[i]];
- }
-#endif
- }
-
- for (i = 0; i < return_value; ++i) {
-#if defined SUPPORT_PCRE2_8 && defined SUPPORT_PCRE2_16
- if (ovector8_1[i] != ovector8_2[i] || ovector8_1[i] != ovector16_1[i] || ovector8_1[i] != ovector16_2[i]) {
- printf("\n8 and 16 bit: Ovector[%d] value differs(J8:%d,I8:%d,J16:%d,I16:%d): [%d] '%s' @ '%s' \n",
- i, ovector8_1[i], ovector8_2[i], ovector16_1[i], ovector16_2[i],
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
-#if defined SUPPORT_PCRE2_8 && defined SUPPORT_PCRE2_32
- if (ovector8_1[i] != ovector8_2[i] || ovector8_1[i] != ovector32_1[i] || ovector8_1[i] != ovector32_2[i]) {
- printf("\n8 and 32 bit: Ovector[%d] value differs(J8:%d,I8:%d,J32:%d,I32:%d): [%d] '%s' @ '%s' \n",
- i, ovector8_1[i], ovector8_2[i], ovector32_1[i], ovector32_2[i],
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
-#if defined SUPPORT_PCRE2_16 && defined SUPPORT_PCRE2_32
- if (ovector16_1[i] != ovector16_2[i] || ovector16_1[i] != ovector32_1[i] || ovector16_1[i] != ovector32_2[i]) {
- printf("\n16 and 32 bit: Ovector[%d] value differs(J16:%d,I16:%d,J32:%d,I32:%d): [%d] '%s' @ '%s' \n",
- i, ovector16_1[i], ovector16_2[i], ovector32_1[i], ovector32_2[i],
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
- }
- }
- } else
-#endif /* more than one of SUPPORT_PCRE2_8, SUPPORT_PCRE2_16 and SUPPORT_PCRE2_32 */
- {
-#ifdef SUPPORT_PCRE2_8
- if (return_value8[0] != return_value8[1]) {
- printf("\n8 bit: Return value differs(%d:%d): [%d] '%s' @ '%s'\n",
- return_value8[0], return_value8[1], total, current->pattern, current->input);
- is_successful = 0;
- } else if (return_value8[0] >= 0 || return_value8[0] == PCRE2_ERROR_PARTIAL) {
- if (return_value8[0] == PCRE2_ERROR_PARTIAL)
- return_value8[0] = 2;
- else
- return_value8[0] *= 2;
-
- for (i = 0; i < return_value8[0]; ++i)
- if (ovector8_1[i] != ovector8_2[i]) {
- printf("\n8 bit: Ovector[%d] value differs(%d:%d): [%d] '%s' @ '%s'\n",
- i, (int)ovector8_1[i], (int)ovector8_2[i], total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
-
-#ifdef SUPPORT_PCRE2_16
- if (return_value16[0] != return_value16[1]) {
- printf("\n16 bit: Return value differs(%d:%d): [%d] '%s' @ '%s'\n",
- return_value16[0], return_value16[1], total, current->pattern, current->input);
- is_successful = 0;
- } else if (return_value16[0] >= 0 || return_value16[0] == PCRE2_ERROR_PARTIAL) {
- if (return_value16[0] == PCRE2_ERROR_PARTIAL)
- return_value16[0] = 2;
- else
- return_value16[0] *= 2;
-
- for (i = 0; i < return_value16[0]; ++i)
- if (ovector16_1[i] != ovector16_2[i]) {
- printf("\n16 bit: Ovector[%d] value differs(%d:%d): [%d] '%s' @ '%s'\n",
- i, (int)ovector16_1[i], (int)ovector16_2[i], total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
-
-#ifdef SUPPORT_PCRE2_32
- if (return_value32[0] != return_value32[1]) {
- printf("\n32 bit: Return value differs(%d:%d): [%d] '%s' @ '%s'\n",
- return_value32[0], return_value32[1], total, current->pattern, current->input);
- is_successful = 0;
- } else if (return_value32[0] >= 0 || return_value32[0] == PCRE2_ERROR_PARTIAL) {
- if (return_value32[0] == PCRE2_ERROR_PARTIAL)
- return_value32[0] = 2;
- else
- return_value32[0] *= 2;
-
- for (i = 0; i < return_value32[0]; ++i)
- if (ovector32_1[i] != ovector32_2[i]) {
- printf("\n32 bit: Ovector[%d] value differs(%d:%d): [%d] '%s' @ '%s'\n",
- i, (int)ovector32_1[i], (int)ovector32_2[i], total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
- }
- }
-
- if (is_successful) {
-#ifdef SUPPORT_PCRE2_8
- if (!(current->start_offset & F_NO8) && (utf || is_ascii)) {
- if (return_value8[0] < 0 && !(current->start_offset & F_NOMATCH)) {
- printf("8 bit: Test should match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-
- if (return_value8[0] >= 0 && (current->start_offset & F_NOMATCH)) {
- printf("8 bit: Test should not match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
-#ifdef SUPPORT_PCRE2_16
- if (!(current->start_offset & F_NO16) && (utf || is_ascii)) {
- if (return_value16[0] < 0 && !(current->start_offset & F_NOMATCH)) {
- printf("16 bit: Test should match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-
- if (return_value16[0] >= 0 && (current->start_offset & F_NOMATCH)) {
- printf("16 bit: Test should not match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
-#ifdef SUPPORT_PCRE2_32
- if (!(current->start_offset & F_NO32) && (utf || is_ascii)) {
- if (return_value32[0] < 0 && !(current->start_offset & F_NOMATCH)) {
- printf("32 bit: Test should match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-
- if (return_value32[0] >= 0 && (current->start_offset & F_NOMATCH)) {
- printf("32 bit: Test should not match: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
- }
-#endif
- }
-
- if (is_successful) {
-#ifdef SUPPORT_PCRE2_8
- if (re8 && !(current->start_offset & F_NO8) && pcre2_get_mark_8(mdata8_1) != pcre2_get_mark_8(mdata8_2)) {
- printf("8 bit: Mark value mismatch: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
-#ifdef SUPPORT_PCRE2_16
- if (re16 && !(current->start_offset & F_NO16) && pcre2_get_mark_16(mdata16_1) != pcre2_get_mark_16(mdata16_2)) {
- printf("16 bit: Mark value mismatch: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
-#ifdef SUPPORT_PCRE2_32
- if (re32 && !(current->start_offset & F_NO32) && pcre2_get_mark_32(mdata32_1) != pcre2_get_mark_32(mdata32_2)) {
- printf("32 bit: Mark value mismatch: [%d] '%s' @ '%s'\n",
- total, current->pattern, current->input);
- is_successful = 0;
- }
-#endif
- }
-
-#ifdef SUPPORT_PCRE2_8
- pcre2_code_free_8(re8);
- pcre2_match_data_free_8(mdata8_1);
- pcre2_match_data_free_8(mdata8_2);
- pcre2_match_context_free_8(mcontext8);
-#endif
-#ifdef SUPPORT_PCRE2_16
- pcre2_code_free_16(re16);
- pcre2_match_data_free_16(mdata16_1);
- pcre2_match_data_free_16(mdata16_2);
- pcre2_match_context_free_16(mcontext16);
-#endif
-#ifdef SUPPORT_PCRE2_32
- pcre2_code_free_32(re32);
- pcre2_match_data_free_32(mdata32_1);
- pcre2_match_data_free_32(mdata32_2);
- pcre2_match_context_free_32(mcontext32);
-#endif
-
- if (is_successful) {
- successful++;
- successful_row++;
- printf(".");
- if (successful_row >= 60) {
- successful_row = 0;
- printf("\n");
- }
- } else
- successful_row = 0;
-
- fflush(stdout);
- current++;
- }
-#ifdef SUPPORT_PCRE2_8
- setstack8(NULL);
-#endif
-#ifdef SUPPORT_PCRE2_16
- setstack16(NULL);
-#endif
-#ifdef SUPPORT_PCRE2_32
- setstack32(NULL);
-#endif
-
- if (total == successful) {
- printf("\nAll JIT regression tests are successfully passed.\n");
- return 0;
- } else {
- printf("\nSuccessful test ratio: %d%% (%d failed)\n", successful * 100 / total, total - successful);
- return 1;
- }
-}
-
-/* End of pcre2_jit_test.c */
diff --git a/thirdparty/pcre2/src/pcre2_match.c b/thirdparty/pcre2/src/pcre2_match.c
index 78a9bacbc8..79cc93f918 100644
--- a/thirdparty/pcre2/src/pcre2_match.c
+++ b/thirdparty/pcre2/src/pcre2_match.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2015-2018 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -43,17 +43,31 @@ POSSIBILITY OF SUCH DAMAGE.
#include "config.h"
#endif
-#define NLBLOCK mb /* Block containing newline information */
-#define PSSTART start_subject /* Field containing processed string start */
-#define PSEND end_subject /* Field containing processed string end */
+/* These defines enables debugging code */
+
+//#define DEBUG_FRAMES_DISPLAY
+//#define DEBUG_SHOW_OPS
+//#define DEBUG_SHOW_RMATCH
+
+#ifdef DEBUG_FRAME_DISPLAY
+#include <stdarg.h>
+#endif
+
+/* These defines identify the name of the block containing "static"
+information, and fields within it. */
+
+#define NLBLOCK mb /* Block containing newline information */
+#define PSSTART start_subject /* Field containing processed string start */
+#define PSEND end_subject /* Field containing processed string end */
#include "pcre2_internal.h"
-/* Masks for identifying the public options that are permitted at match time.
-*/
+#define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */
+
+/* Masks for identifying the public options that are permitted at match time. */
#define PUBLIC_MATCH_OPTIONS \
- (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT)
@@ -61,60 +75,255 @@ POSSIBILITY OF SUCH DAMAGE.
(PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\
PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD)
-/* The mb->capture_last field uses the lower 16 bits for the last captured
-substring (which can never be greater than 65535) and a bit in the top half
-to mean "capture vector overflowed". This odd way of doing things was
-implemented when it was realized that preserving and restoring the overflow bit
-whenever the last capture number was saved/restored made for a neater
-interface, and doing it this way saved on (a) another variable, which would
-have increased the stack frame size (a big NO-NO in PCRE) and (b) another
-separate set of save/restore instructions. The following defines are used in
-implementing this. */
-
-#define CAPLMASK 0x0000ffff /* The bits used for last_capture */
-#define OVFLMASK 0xffff0000 /* The bits used for the overflow flag */
-#define OVFLBIT 0x00010000 /* The bit that is set for overflow */
-
-/* Bits for setting in mb->match_function_type to indicate two special types
-of call to match(). We do it this way to save on using another stack variable,
-as stack usage is to be discouraged. */
-
-#define MATCH_CONDASSERT 1 /* Called to check a condition assertion */
-#define MATCH_CBEGROUP 2 /* Could-be-empty unlimited repeat group */
-
-/* Non-error returns from the match() function. Error returns are externally
-defined PCRE2_ERROR_xxx codes, which are all negative. */
+/* Non-error returns from and within the match() function. Error returns are
+externally defined PCRE2_ERROR_xxx codes, which are all negative. */
#define MATCH_MATCH 1
#define MATCH_NOMATCH 0
-/* Special internal returns from the match() function. Make them sufficiently
-negative to avoid the external error codes. */
+/* Special internal returns used in the match() function. Make them
+sufficiently negative to avoid the external error codes. */
#define MATCH_ACCEPT (-999)
#define MATCH_KETRPOS (-998)
-#define MATCH_ONCE (-997)
/* The next 5 must be kept together and in sequence so that a test that checks
for any one of them can use a range. */
-#define MATCH_COMMIT (-996)
-#define MATCH_PRUNE (-995)
-#define MATCH_SKIP (-994)
-#define MATCH_SKIP_ARG (-993)
-#define MATCH_THEN (-992)
+#define MATCH_COMMIT (-997)
+#define MATCH_PRUNE (-996)
+#define MATCH_SKIP (-995)
+#define MATCH_SKIP_ARG (-994)
+#define MATCH_THEN (-993)
#define MATCH_BACKTRACK_MAX MATCH_THEN
#define MATCH_BACKTRACK_MIN MATCH_COMMIT
-/* Min and max values for the common repeats; for the maxima, 0 => infinity */
+/* Group frame type values. Zero means the frame is not a group frame. The
+lower 16 bits are used for data (e.g. the capture number). Group frames are
+used for most groups so that information about the start is easily available at
+the end without having to scan back through intermediate frames (backtrack
+points). */
+
+#define GF_CAPTURE 0x00010000u
+#define GF_NOCAPTURE 0x00020000u
+#define GF_CONDASSERT 0x00030000u
+#define GF_RECURSE 0x00040000u
-static const char rep_min[] = { 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, };
-static const char rep_max[] = { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, };
+/* Masks for the identity and data parts of the group frame type. */
-/* Maximum number of ovector elements that can be saved on the system stack
-when processing OP_RECURSE in non-HEAP_MATCH_RECURSE mode. If the ovector is
-bigger, malloc() is used. This value should be a multiple of 3, because the
-ovector length is always a multiple of 3. */
+#define GF_IDMASK(a) ((a) & 0xffff0000u)
+#define GF_DATAMASK(a) ((a) & 0x0000ffffu)
-#define OP_RECURSE_STACK_SAVE_MAX 45
+/* Repetition types */
+
+enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS };
+
+/* Min and max values for the common repeats; a maximum of UINT32_MAX =>
+infinity. */
+
+static const uint32_t rep_min[] = {
+ 0, 0, /* * and *? */
+ 1, 1, /* + and +? */
+ 0, 0, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+static const uint32_t rep_max[] = {
+ UINT32_MAX, UINT32_MAX, /* * and *? */
+ UINT32_MAX, UINT32_MAX, /* + and +? */
+ 1, 1, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+/* Repetition types - must include OP_CRPOSRANGE (not needed above) */
+
+static const uint32_t rep_typ[] = {
+ REPTYPE_MAX, REPTYPE_MIN, /* * and *? */
+ REPTYPE_MAX, REPTYPE_MIN, /* + and +? */
+ REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */
+ REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */
+ REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */
+ REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */
+
+/* Numbers for RMATCH calls at backtracking points. When these lists are
+changed, the code at RETURN_SWITCH below must be updated in sync. */
+
+enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10,
+ RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,
+ RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,
+ RM31, RM32, RM33, RM34, RM35 };
+
+#ifdef SUPPORT_WIDE_CHARS
+enum { RM100=100, RM101 };
+#endif
+
+#ifdef SUPPORT_UNICODE
+enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207,
+ RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215,
+ RM216, RM217, RM218, RM219, RM220, RM221, RM222 };
+#endif
+
+/* Define short names for general fields in the current backtrack frame, which
+is always pointed to by the F variable. Occasional references to fields in
+other frames are written out explicitly. There are also some fields in the
+current frame whose names start with "temp" that are used for short-term,
+localised backtracking memory. These are #defined with Lxxx names at the point
+of use and undefined afterwards. */
+
+#define Fback_frame F->back_frame
+#define Fcapture_last F->capture_last
+#define Fcurrent_recurse F->current_recurse
+#define Fecode F->ecode
+#define Feptr F->eptr
+#define Fgroup_frame_type F->group_frame_type
+#define Flast_group_offset F->last_group_offset
+#define Flength F->length
+#define Fmark F->mark
+#define Frdepth F->rdepth
+#define Fstart_match F->start_match
+#define Foffset_top F->offset_top
+#define Foccu F->occu
+#define Fop F->op
+#define Fovector F->ovector
+#define Freturn_id F->return_id
+
+
+#ifdef DEBUG_FRAMES_DISPLAY
+/*************************************************
+* Display current frames and contents *
+*************************************************/
+
+/* This debugging function displays the current set of frames and their
+contents. It is not called automatically from anywhere, the intention being
+that calls can be inserted where necessary when debugging frame-related
+problems.
+
+Arguments:
+ f the file to write to
+ F the current top frame
+ P a previous frame of interest
+ frame_size the frame size
+ mb points to the match block
+ s identification text
+
+Returns: nothing
+*/
+
+static void
+display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size,
+ match_block *mb, const char *s, ...)
+{
+uint32_t i;
+heapframe *Q;
+va_list ap;
+va_start(ap, s);
+
+fprintf(f, "FRAMES ");
+vfprintf(f, s, ap);
+va_end(ap);
+
+if (P != NULL) fprintf(f, " P=%lu",
+ ((char *)P - (char *)(mb->match_frames))/frame_size);
+fprintf(f, "\n");
+
+for (i = 0, Q = mb->match_frames;
+ Q <= F;
+ i++, Q = (heapframe *)((char *)Q + frame_size))
+ {
+ fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d",
+ i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode),
+ Q->back_frame, Q->return_id);
+
+ if (Q->last_group_offset == PCRE2_UNSET)
+ fprintf(f, " lgoffset=unset\n");
+ else
+ fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size);
+ }
+}
+
+#endif
+
+
+
+/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called for all callouts, whether "standalone" or at the
+start of a conditional group. Feptr will be pointing to either OP_CALLOUT or
+OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized
+with fixed values.
+
+Arguments:
+ F points to the current backtracking frame
+ mb points to the match block
+ lengthptr where to return the length of the callout item
+
+Returns: the return from the callout
+ or 0 if no callout function exists
+*/
+
+static int
+do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr)
+{
+int rc;
+PCRE2_SIZE save0, save1;
+PCRE2_SIZE *callout_ovector;
+pcre2_callout_block *cb;
+
+*lengthptr = (*Fecode == OP_CALLOUT)?
+ PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE);
+
+if (mb->callout == NULL) return 0; /* No callout function provided */
+
+/* The original matching code (pre 10.30) worked directly with the ovector
+passed by the user, and this was passed to callouts. Now that the working
+ovector is in the backtracking frame, it no longer needs to reserve space for
+the overall match offsets (which would waste space in the frame). For backward
+compatibility, however, we pass capture_top and offset_vector to the callout as
+if for the extended ovector, and we ensure that the first two slots are unset
+by preserving and restoring their current contents. Picky compilers complain if
+references such as Fovector[-2] are use directly, so we set up a separate
+pointer. */
+
+callout_ovector = (PCRE2_SIZE *)(Fovector) - 2;
+
+/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields
+are set externally. The first 3 never change; the last is updated for each
+bumpalong. */
+
+cb = mb->cb;
+cb->capture_top = (uint32_t)Foffset_top/2 + 1;
+cb->capture_last = Fcapture_last;
+cb->offset_vector = callout_ovector;
+cb->mark = mb->nomatch_mark;
+cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject);
+cb->pattern_position = GET(Fecode, 1);
+cb->next_item_length = GET(Fecode, 1 + LINK_SIZE);
+
+if (*Fecode == OP_CALLOUT) /* Numerical callout */
+ {
+ cb->callout_number = Fecode[1 + 2*LINK_SIZE];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else /* String callout */
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE);
+ cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1;
+ cb->callout_string_length =
+ *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+save0 = callout_ovector[0];
+save1 = callout_ovector[1];
+callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET;
+rc = mb->callout(cb, mb->callout_data);
+callout_ovector[0] = save0;
+callout_ovector[1] = save1;
+cb->callout_flags = 0;
+return rc;
+}
@@ -130,10 +339,9 @@ seems unlikely.)
Arguments:
offset index into the offset vector
- offset_top top of the used offset vector
- eptr pointer into the subject
- mb points to match block
caseless TRUE if caseless
+ F the current backtracking frame pointer
+ mb points to match block
lengthptr pointer for returning the length matched
Returns: = 0 sucessful match; number of code units matched is set
@@ -142,21 +350,18 @@ Returns: = 0 sucessful match; number of code units matched is set
*/
static int
-match_ref(PCRE2_SIZE offset, PCRE2_SIZE offset_top, PCRE2_SPTR eptr,
- match_block *mb, BOOL caseless, PCRE2_SIZE *lengthptr)
+match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb,
+ PCRE2_SIZE *lengthptr)
{
-#if defined SUPPORT_UNICODE
-BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
-#endif
-
PCRE2_SPTR p;
PCRE2_SIZE length;
-PCRE2_SPTR eptr_start = eptr;
+PCRE2_SPTR eptr;
+PCRE2_SPTR eptr_start;
/* Deal with an unset group. The default is no match, but there is an option to
match an empty string. */
-if (offset >= offset_top || mb->ovector[offset] == PCRE2_UNSET)
+if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET)
{
if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
{
@@ -168,19 +373,20 @@ if (offset >= offset_top || mb->ovector[offset] == PCRE2_UNSET)
/* Separate the caseless and UTF cases for speed. */
-p = mb->start_subject + mb->ovector[offset];
-length = mb->ovector[offset+1] - mb->ovector[offset];
+eptr = eptr_start = Feptr;
+p = mb->start_subject + Fovector[offset];
+length = Fovector[offset+1] - Fovector[offset];
if (caseless)
{
#if defined SUPPORT_UNICODE
- if (utf)
+ if ((mb->poptions & PCRE2_UTF) != 0)
{
/* Match characters up to the end of the reference. NOTE: the number of
code units matched may differ, because in UTF-8 there are some characters
- whose upper and lower case versions code have different numbers of bytes.
- For example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65
- (3 bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a
+ whose upper and lower case codes have different numbers of bytes. For
+ example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3
+ bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a
sequence of two of the latter. It is important, therefore, to check the
length along the reference, not along the subject (earlier code did this
wrong). */
@@ -226,14 +432,26 @@ if (caseless)
}
/* In the caseful case, we can just compare the code units, whether or not we
-are in UTF mode. */
+are in UTF mode. When partial matching, we have to do this unit-by-unit. */
else
{
- for (; length > 0; length--)
+ if (mb->partial != 0)
+ {
+ for (; length > 0; length--)
+ {
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+ if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */
+ }
+ }
+
+ /* Not partial matching */
+
+ else
{
- if (eptr >= mb->end_subject) return 1; /* Partial match */
- if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /*No match */
+ if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */
+ if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */
+ eptr += length;
}
}
@@ -243,278 +461,73 @@ return 0; /* Match */
-/***************************************************************************
-****************************************************************************
- RECURSION IN THE match() FUNCTION
-
-The match() function is highly recursive, though not every recursive call
-increases the recursion depth. Nevertheless, some regular expressions can cause
-it to recurse to a great depth. I was writing for Unix, so I just let it call
-itself recursively. This uses the stack for saving everything that has to be
-saved for a recursive call. On Unix, the stack can be large, and this works
-fine.
-
-It turns out that on some non-Unix-like systems there are problems with
-programs that use a lot of stack. (This despite the fact that every last chip
-has oodles of memory these days, and techniques for extending the stack have
-been known for decades.) So....
-
-There is a fudge, triggered by defining HEAP_MATCH_RECURSE, which avoids
-recursive calls by keeping local variables that need to be preserved in blocks
-of memory on the heap instead instead of on the stack. Macros are used to
-achieve this so that the actual code doesn't look very different to what it
-always used to.
-
-The original heap-recursive code used longjmp(). However, it seems that this
-can be very slow on some operating systems. Following a suggestion from Stan
-Switzer, the use of longjmp() has been abolished, at the cost of having to
-provide a unique number for each call to RMATCH. There is no way of generating
-a sequence of numbers at compile time in C. I have given them names, to make
-them stand out more clearly.
-
-Crude tests on x86 Linux show a small speedup of around 5-8%. However, on
-FreeBSD, avoiding longjmp() more than halves the time taken to run the standard
-tests. Furthermore, not using longjmp() means that local dynamic variables
-don't have indeterminate values; this has meant that the frame size can be
-reduced because the result can be "passed back" by straight setting of the
-variable instead of being passed in the frame.
-****************************************************************************
-***************************************************************************/
-
-/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN
-below must be updated in sync. */
-
-enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10,
- RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,
- RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,
- RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40,
- RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50,
- RM51, RM52, RM53, RM54, RM55, RM56, RM57, RM58, RM59, RM60,
- RM61, RM62, RM63, RM64, RM65, RM66, RM67, RM68 };
-
-/* These versions of the macros use the stack, as normal. Note that the "rw"
-argument of RMATCH isn't actually used in this definition. */
-
-#ifndef HEAP_MATCH_RECURSE
-#define RMATCH(ra,rb,rc,rd,re,rw) \
- rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1)
-#define RRETURN(ra) return ra
-#else
-
-/* These versions of the macros manage a private stack on the heap. Note that
-the "rd" argument of RMATCH isn't actually used in this definition. It's the mb
-argument of match(), which never changes. */
-
-#define RMATCH(ra,rb,rc,rd,re,rw)\
- {\
- heapframe *newframe = frame->Xnextframe;\
- if (newframe == NULL)\
- {\
- newframe = (heapframe *)(mb->stack_memctl.malloc)\
- (sizeof(heapframe), mb->stack_memctl.memory_data);\
- if (newframe == NULL) RRETURN(PCRE2_ERROR_NOMEMORY);\
- newframe->Xnextframe = NULL;\
- frame->Xnextframe = newframe;\
- }\
- frame->Xwhere = rw;\
- newframe->Xeptr = ra;\
- newframe->Xecode = rb;\
- newframe->Xmstart = mstart;\
- newframe->Xoffset_top = rc;\
- newframe->Xeptrb = re;\
- newframe->Xrdepth = frame->Xrdepth + 1;\
- newframe->Xprevframe = frame;\
- frame = newframe;\
- goto HEAP_RECURSE;\
- L_##rw:;\
- }
-
-#define RRETURN(ra)\
- {\
- heapframe *oldframe = frame;\
- frame = oldframe->Xprevframe;\
- if (frame != NULL)\
- {\
- rrc = ra;\
- goto HEAP_RETURN;\
- }\
- return ra;\
- }
-
-
-/* Structure for remembering the local variables in a private frame. Arrange it
-so as to minimize the number of holes. */
-
-typedef struct heapframe {
- struct heapframe *Xprevframe;
- struct heapframe *Xnextframe;
-
-#ifdef SUPPORT_UNICODE
- PCRE2_SPTR Xcharptr;
-#endif
- PCRE2_SPTR Xeptr;
- PCRE2_SPTR Xecode;
- PCRE2_SPTR Xmstart;
- PCRE2_SPTR Xcallpat;
- PCRE2_SPTR Xdata;
- PCRE2_SPTR Xnext_ecode;
- PCRE2_SPTR Xpp;
- PCRE2_SPTR Xprev;
- PCRE2_SPTR Xsaved_eptr;
-
- eptrblock *Xeptrb;
-
- PCRE2_SIZE Xlength;
- PCRE2_SIZE Xoffset;
- PCRE2_SIZE Xoffset_top;
- PCRE2_SIZE Xsave_offset1, Xsave_offset2, Xsave_offset3;
-
- uint32_t Xfc;
- uint32_t Xnumber;
- uint32_t Xrdepth;
- uint32_t Xop;
- uint32_t Xsave_capture_last;
-
-#ifdef SUPPORT_UNICODE
- uint32_t Xprop_value;
- int Xprop_type;
- int Xprop_fail_result;
- int Xoclength;
-#endif
-
- int Xcodelink;
- int Xctype;
- int Xfi;
- int Xmax;
- int Xmin;
- int Xwhere; /* Where to jump back to */
-
- BOOL Xcondition;
- BOOL Xcur_is_word;
- BOOL Xprev_is_word;
+/******************************************************************************
+*******************************************************************************
+ "Recursion" in the match() function
- eptrblock Xnewptrb;
- recursion_info Xnew_recursive;
+The original match() function was highly recursive, but this proved to be the
+source of a number of problems over the years, mostly because of the relatively
+small system stacks that are commonly found. As new features were added to
+patterns, various kludges were invented to reduce the amount of stack used,
+making the code hard to understand in places.
-#ifdef SUPPORT_UNICODE
- PCRE2_UCHAR Xocchars[6];
-#endif
-} heapframe;
-
-#endif
-
-
-/***************************************************************************
-***************************************************************************/
+A version did exist that used individual frames on the heap instead of calling
+match() recursively, but this ran substantially slower. The current version is
+a refactoring that uses a vector of frames to remember backtracking points.
+This runs no slower, and possibly even a bit faster than the original recursive
+implementation. An initial vector of size START_FRAMES_SIZE (enough for maybe
+50 frames) is allocated on the system stack. If this is not big enough, the
+heap is used for a larger vector.
+*******************************************************************************
+******************************************************************************/
-/* When HEAP_MATCH_RECURSE is not defined, the match() function implements
-backtrack points by calling itself recursively in all but one case. The one
-special case is when processing OP_RECURSE, which specifies recursion in the
-pattern. The entire ovector must be saved and restored while processing
-OP_RECURSE. If the ovector is small enough, instead of calling match()
-directly, op_recurse_ovecsave() is called. This function uses the system stack
-to save the ovector while calling match() to process the pattern recursion. */
-#ifndef HEAP_MATCH_RECURSE
-
-/* We need a prototype for match() because it is mutually recursive with
-op_recurse_ovecsave(). */
-
-static int
-match(PCRE2_SPTR eptr, PCRE2_SPTR ecode, PCRE2_SPTR mstart,
- PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth);
/*************************************************
-* Process OP_RECURSE, stacking ovector *
+* Macros for the match() function *
*************************************************/
-/* When this function is called, mb->recursive has already been updated to
-point to a new recursion data block, and all its fields other than ovec_save
-have been set.
+/* These macros pack up tests that are used for partial matching several times
+in the code. We set the "hit end" flag if the pointer is at the end of the
+subject and also past the earliest inspected character (i.e. something has been
+matched, even if not part of the actual matched string). For hard partial
+matching, we then return immediately. The second one is used when we already
+know we are past the end of the subject. */
-This function exists so that the local vector variable ovecsave is no longer
-defined in the match() function, as it was in PCRE1. It is used only when there
-is recursion in the pattern, so it wastes a lot of stack to have it defined for
-every call of match(). We now use this function as an indirect way of calling
-match() only in the case when ovecsave is needed. (David Wheeler used to say
-"All problems in computer science can be solved by another level of
-indirection.")
-
-HOWEVER: when this file is compiled by gcc in an optimizing mode, because this
-function is called only once, and only from within match(), gcc will "inline"
-it - that is, move it inside match() - and this completely negates its reason
-for existence. Therefore, we mark it as non-inline when gcc is in use.
-
-Arguments:
- eptr pointer to current character in subject
- callpat the recursion point in the pattern
- mstart pointer to the current match start position (can be modified
- by encountering \K)
- offset_top current top pointer (highest ovector offset used + 1)
- mb pointer to "static" info block for the match
- eptrb pointer to chain of blocks containing eptr at start of
- brackets - for testing for empty matches
- rdepth the recursion depth
-
-Returns: a match() return code
-*/
-
-static int
-#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
-__attribute__ ((noinline))
-#endif
-op_recurse_ovecsave(PCRE2_SPTR eptr, PCRE2_SPTR callpat,
- PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb,
- uint32_t rdepth)
-{
-int rrc;
-BOOL cbegroup = *callpat >= OP_SBRA;
-recursion_info *new_recursive = mb->recursive;
-PCRE2_SIZE ovecsave[OP_RECURSE_STACK_SAVE_MAX];
-
-/* Save the ovector */
+#define CHECK_PARTIAL()\
+ if (mb->partial != 0 && Feptr >= mb->end_subject && \
+ Feptr > mb->start_used_ptr) \
+ { \
+ mb->hitend = TRUE; \
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \
+ }
-new_recursive->ovec_save = ovecsave;
-memcpy(ovecsave, mb->ovector, mb->offset_end * sizeof(PCRE2_SIZE));
+#define SCHECK_PARTIAL()\
+ if (mb->partial != 0 && Feptr > mb->start_used_ptr) \
+ { \
+ mb->hitend = TRUE; \
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \
+ }
-/* Do the recursion. After processing each alternative, restore the ovector
-data and the last captured value. */
+/* These macros are used to implement backtracking. They simulate a recursive
+call to the match() function by means of a local vector of frames which
+remember the backtracking points. */
-do
- {
- if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP;
- rrc = match(eptr, callpat + PRIV(OP_lengths)[*callpat], mstart, offset_top,
- mb, eptrb, rdepth + 1);
- memcpy(mb->ovector, new_recursive->ovec_save,
- mb->offset_end * sizeof(PCRE2_SIZE));
- mb->capture_last = new_recursive->saved_capture_last;
- if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) return rrc;
-
- /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a
- recursion; they cause a NOMATCH for the entire recursion. These codes
- are defined in a range that can be tested for. */
-
- if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX)
- return MATCH_NOMATCH;
-
- /* Any return code other than NOMATCH is an error. Otherwise, advance to the
- next alternative or to the end of the recursing subpattern. If there were
- nested recursions, mb->recursive might be changed, so reset it before
- looping. */
-
- if (rrc != MATCH_NOMATCH) return rrc;
- mb->recursive = new_recursive;
- callpat += GET(callpat, 1);
+#define RMATCH(ra,rb)\
+ {\
+ start_ecode = ra;\
+ Freturn_id = rb;\
+ goto MATCH_RECURSE;\
+ L_##rb:;\
}
-while (*callpat == OP_ALT); /* Loop for the alternatives */
-
-/* None of the alternatives matched. */
-return MATCH_NOMATCH;
-}
-#endif /* HEAP_MATCH_RECURSE */
+#define RRETURN(ra)\
+ {\
+ rrc = ra;\
+ goto RETURN_SWITCH;\
+ }
@@ -522,2468 +535,1270 @@ return MATCH_NOMATCH;
* Match from current position *
*************************************************/
-/* This function is called recursively in many circumstances. Whenever it
-returns a negative (error) response, the outer incarnation must also return the
-same response. */
+/* This function is called to run one match attempt at a single starting point
+in the subject.
-/* These macros pack up tests that are used for partial matching, and which
-appear several times in the code. We set the "hit end" flag if the pointer is
-at the end of the subject and also past the earliest inspected character (i.e.
-something has been matched, even if not part of the actual matched string). For
-hard partial matching, we then return immediately. The second one is used when
-we already know we are past the end of the subject. */
-
-#define CHECK_PARTIAL()\
- if (mb->partial != 0 && eptr >= mb->end_subject && \
- eptr > mb->start_used_ptr) \
- { \
- mb->hitend = TRUE; \
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \
- }
-
-#define SCHECK_PARTIAL()\
- if (mb->partial != 0 && eptr > mb->start_used_ptr) \
- { \
- mb->hitend = TRUE; \
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \
- }
-
-
-/* Performance note: It might be tempting to extract commonly used fields from
-the mb structure (e.g. utf, end_subject) into individual variables to improve
+Performance note: It might be tempting to extract commonly used fields from the
+mb structure (e.g. end_subject) into individual variables to improve
performance. Tests using gcc on a SPARC disproved this; in the first case, it
made performance worse.
Arguments:
- eptr pointer to current character in subject
- ecode pointer to current position in compiled code
- mstart pointer to the current match start position (can be modified
- by encountering \K)
- offset_top current top pointer (highest ovector offset used + 1)
- mb pointer to "static" info block for the match
- eptrb pointer to chain of blocks containing eptr at start of
- brackets - for testing for empty matches
- rdepth the recursion depth
-
-Returns: MATCH_MATCH if matched ) these values are >= 0
- MATCH_NOMATCH if failed to match )
- a negative MATCH_xxx value for PRUNE, SKIP, etc
- a negative PCRE2_ERROR_xxx value if aborted by an error condition
- (e.g. stopped by repeated call or recursion limit)
+ start_eptr starting character in subject
+ start_ecode starting position in compiled code
+ ovector pointer to the final output vector
+ oveccount number of pairs in ovector
+ top_bracket number of capturing parentheses in the pattern
+ frame_size size of each backtracking frame
+ mb pointer to "static" variables block
+
+Returns: MATCH_MATCH if matched ) these values are >= 0
+ MATCH_NOMATCH if failed to match )
+ negative MATCH_xxx value for PRUNE, SKIP, etc
+ negative PCRE2_ERROR_xxx value if aborted by an error condition
+ (e.g. stopped by repeated call or depth limit)
*/
static int
-match(PCRE2_SPTR eptr, PCRE2_SPTR ecode, PCRE2_SPTR mstart,
- PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth)
+match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, PCRE2_SIZE *ovector,
+ uint16_t oveccount, uint16_t top_bracket, PCRE2_SIZE frame_size,
+ match_block *mb)
{
-/* These variables do not need to be preserved over recursion in this function,
-so they can be ordinary variables in all cases. Mark some of them with
-"register" because they are used a lot in loops. */
-
-int rrc; /* Returns from recursive calls */
-int i; /* Used for loops not involving calls to RMATCH() */
-uint32_t c; /* Character values not kept over RMATCH() calls */
-BOOL utf; /* Local copy of UTF flag for speed */
-
-BOOL minimize, possessive; /* Quantifier options */
-int condcode;
+/* Frame-handling variables */
-/* When recursion is not being used, all "local" variables that have to be
-preserved over calls to RMATCH() are part of a "frame". We set up the top-level
-frame on the stack here; subsequent instantiations are obtained from the heap
-whenever RMATCH() does a "recursion". See the macro definitions above. Putting
-the top-level on the stack rather than malloc-ing them all gives a performance
-boost in many cases where there is not much "recursion". */
+heapframe *F; /* Current frame pointer */
+heapframe *N = NULL; /* Temporary frame pointers */
+heapframe *P = NULL;
+heapframe *assert_accept_frame; /* For passing back the frame with captures */
+PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */
-#ifdef HEAP_MATCH_RECURSE
-heapframe *frame = (heapframe *)mb->match_frames_base;
+/* Local variables that do not need to be preserved over calls to RRMATCH(). */
-/* Copy in the original argument variables */
+PCRE2_SPTR bracode; /* Temp pointer to start of group */
+PCRE2_SIZE offset; /* Used for group offsets */
+PCRE2_SIZE length; /* Used for various length calculations */
-frame->Xeptr = eptr;
-frame->Xecode = ecode;
-frame->Xmstart = mstart;
-frame->Xoffset_top = offset_top;
-frame->Xeptrb = eptrb;
-frame->Xrdepth = rdepth;
-
-/* This is where control jumps back to to effect "recursion" */
-
-HEAP_RECURSE:
+int rrc; /* Return from functions & backtracking "recursions" */
+#ifdef SUPPORT_UNICODE
+int proptype; /* Type of character property */
+#endif
-/* Macros make the argument variables come from the current frame */
+uint32_t i; /* Used for local loops */
+uint32_t fc; /* Character values */
+uint32_t number; /* Used for group and other numbers */
+uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */
+uint32_t group_frame_type; /* Specifies type for new group frames */
-#define eptr frame->Xeptr
-#define ecode frame->Xecode
-#define mstart frame->Xmstart
-#define offset_top frame->Xoffset_top
-#define eptrb frame->Xeptrb
-#define rdepth frame->Xrdepth
+BOOL condition; /* Used in conditional groups */
+BOOL cur_is_word; /* Used in "word" tests */
+BOOL prev_is_word; /* Used in "word" tests */
-/* Ditto for the local variables */
+/* UTF flag */
#ifdef SUPPORT_UNICODE
-#define charptr frame->Xcharptr
-#define prop_value frame->Xprop_value
-#define prop_type frame->Xprop_type
-#define prop_fail_result frame->Xprop_fail_result
-#define oclength frame->Xoclength
-#define occhars frame->Xocchars
+BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+#else
+BOOL utf = FALSE;
#endif
+/* This is the length of the last part of a backtracking frame that must be
+copied when a new frame is created. */
-#define callpat frame->Xcallpat
-#define codelink frame->Xcodelink
-#define data frame->Xdata
-#define next_ecode frame->Xnext_ecode
-#define pp frame->Xpp
-#define prev frame->Xprev
-#define saved_eptr frame->Xsaved_eptr
-
-#define new_recursive frame->Xnew_recursive
-
-#define ctype frame->Xctype
-#define fc frame->Xfc
-#define fi frame->Xfi
-#define length frame->Xlength
-#define max frame->Xmax
-#define min frame->Xmin
-#define number frame->Xnumber
-#define offset frame->Xoffset
-#define op frame->Xop
-#define save_capture_last frame->Xsave_capture_last
-#define save_offset1 frame->Xsave_offset1
-#define save_offset2 frame->Xsave_offset2
-#define save_offset3 frame->Xsave_offset3
-
-#define condition frame->Xcondition
-#define cur_is_word frame->Xcur_is_word
-#define prev_is_word frame->Xprev_is_word
-
-#define newptrb frame->Xnewptrb
-
-/* When normal stack-based recursion is being used for match(), local variables
-are allocated on the stack and get preserved during recursion in the usual way.
-In this environment, fi and i, and fc and c, can be the same variables. */
-
-#else /* HEAP_MATCH_RECURSE not defined */
-#define fi i
-#define fc c
-
-/* Many of the following variables are used only in small blocks of the code.
-My normal style of coding would have declared them within each of those blocks.
-However, in order to accommodate the version of this code that uses an external
-"stack" implemented on the heap, it is easier to declare them all here, so the
-declarations can be cut out in a block. The only declarations within blocks
-below are for variables that do not have to be preserved over a recursive call
-to RMATCH(). */
+frame_copy_size = frame_size - offsetof(heapframe, eptr);
-#ifdef SUPPORT_UNICODE
-PCRE2_SPTR charptr;
-#endif
-PCRE2_SPTR callpat;
-PCRE2_SPTR data;
-PCRE2_SPTR next_ecode;
-PCRE2_SPTR pp;
-PCRE2_SPTR prev;
-PCRE2_SPTR saved_eptr;
+/* Set up the first current frame at the start of the vector, and initialize
+fields that are not reset for new frames. */
-PCRE2_SIZE length;
-PCRE2_SIZE offset;
-PCRE2_SIZE save_offset1, save_offset2, save_offset3;
+F = mb->match_frames;
+Frdepth = 0; /* "Recursion" depth */
+Fcapture_last = 0; /* Number of most recent capture */
+Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */
+Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */
+Fmark = NULL; /* Most recent mark */
+Foffset_top = 0; /* End of captures within the frame */
+Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */
+group_frame_type = 0; /* Not a start of group frame */
+goto NEW_FRAME; /* Start processing with this frame */
-uint32_t number;
-uint32_t op;
-uint32_t save_capture_last;
-
-#ifdef SUPPORT_UNICODE
-uint32_t prop_value;
-int prop_type;
-int prop_fail_result;
-int oclength;
-PCRE2_UCHAR occhars[6];
-#endif
+/* Come back here when we want to create a new frame for remembering a
+backtracking point. */
-int codelink;
-int ctype;
-int max;
-int min;
+MATCH_RECURSE:
-BOOL condition;
-BOOL cur_is_word;
-BOOL prev_is_word;
+/* Set up a new backtracking frame. If the vector is full, get a new one
+on the heap, doubling the size, but constrained by the heap limit. */
-eptrblock newptrb;
-recursion_info new_recursive;
-#endif /* HEAP_MATCH_RECURSE not defined */
+N = (heapframe *)((char *)F + frame_size);
+if (N >= mb->match_frames_top)
+ {
+ PCRE2_SIZE newsize = mb->frame_vector_size * 2;
+ heapframe *new;
-/* To save space on the stack and in the heap frame, I have doubled up on some
-of the local variables that are used only in localised parts of the code, but
-still need to be preserved over recursive calls of match(). These macros define
-the alternative names that are used. */
+ if ((newsize / 1024) > mb->heap_limit)
+ {
+ PCRE2_SIZE maxsize = ((mb->heap_limit * 1024)/frame_size) * frame_size;
+ if (mb->frame_vector_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT;
+ newsize = maxsize;
+ }
-#define allow_zero cur_is_word
-#define caseless cur_is_word
-#define cbegroup condition
-#define code_offset codelink
-#define condassert condition
-#define foc number
-#define matched_once prev_is_word
-#define save_mark data
+ new = mb->memctl.malloc(newsize, mb->memctl.memory_data);
+ if (new == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy(new, mb->match_frames, mb->frame_vector_size);
-/* These statements are here to stop the compiler complaining about unitialized
-variables. */
+ F = (heapframe *)((char *)new + ((char *)F - (char *)mb->match_frames));
+ N = (heapframe *)((char *)F + frame_size);
-#ifdef SUPPORT_UNICODE
-prop_value = 0;
-prop_fail_result = 0;
-#endif
+ if (mb->match_frames != mb->stack_frames)
+ mb->memctl.free(mb->match_frames, mb->memctl.memory_data);
+ mb->match_frames = new;
+ mb->match_frames_top = (heapframe *)((char *)mb->match_frames + newsize);
+ mb->frame_vector_size = newsize;
+ }
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1);
+if (group_frame_type != 0)
+ {
+ fprintf(stderr, " type=%x ", group_frame_type);
+ switch (GF_IDMASK(group_frame_type))
+ {
+ case GF_CAPTURE:
+ fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type));
+ break;
-/* This label is used for tail recursion, which is used in a few cases even
-when HEAP_MATCH_RECURSE is not defined, in order to reduce the amount of stack
-that is used. Thanks to Ian Taylor for noticing this possibility and sending
-the original patch. */
+ case GF_NOCAPTURE:
+ fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type));
+ break;
-TAIL_RECURSE:
+ case GF_CONDASSERT:
+ fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type));
+ break;
-/* OK, now we can get on with the real code of the function. Recursive calls
-are specified by the macro RMATCH and RRETURN is used to return. When
-HEAP_MATCH_RECURSE is *not* defined, these just turn into a recursive call to
-match() and a "return", respectively. However, RMATCH isn't like a function
-call because it's quite a complicated macro. It has to be used in one
-particular way. This shouldn't, however, impact performance when true recursion
-is being used. */
+ case GF_RECURSE:
+ fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type));
+ break;
-#ifdef SUPPORT_UNICODE
-utf = (mb->poptions & PCRE2_UTF) != 0;
-#else
-utf = FALSE;
+ default:
+ fprintf(stderr, "*** unknown ***");
+ break;
+ }
+ }
+fprintf(stderr, "\n");
#endif
-/* First check that we haven't called match() too many times, or that we
-haven't exceeded the recursive call limit. */
+/* Copy those fields that must be copied into the new frame, increase the
+"recursion" depth (i.e. the new frame's index) and then make the new frame
+current. */
+
+memcpy((char *)N + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+
+N->rdepth = Frdepth + 1;
+F = N;
-if (mb->match_call_count++ >= mb->match_limit) RRETURN(PCRE2_ERROR_MATCHLIMIT);
-if (rdepth >= mb->match_limit_recursion) RRETURN(PCRE2_ERROR_RECURSIONLIMIT);
+/* Carry on processing with a new frame. */
-/* At the start of a group with an unlimited repeat that may match an empty
-string, the variable mb->match_function_type contains the MATCH_CBEGROUP bit.
-It is done this way to save having to use another function argument, which
-would take up space on the stack. See also MATCH_CONDASSERT below.
+NEW_FRAME:
+Fgroup_frame_type = group_frame_type;
+Fecode = start_ecode; /* Starting code pointer */
+Fback_frame = frame_size; /* Default is go back one frame */
-When MATCH_CBEGROUP is set, add the current subject pointer to the chain of
-such remembered pointers, to be checked when we hit the closing ket, in order
-to break infinite loops that match no characters. When match() is called in
-other circumstances, don't add to the chain. The MATCH_CBEGROUP feature must
-NOT be used with tail recursion, because the memory block that is used is on
-the stack, so a new one may be required for each match(). */
+/* If this is a special type of group frame, remember its offset for quick
+access at the end of the group. If this is a recursion, set a new current
+recursion value. */
-if ((mb->match_function_type & MATCH_CBEGROUP) != 0)
+if (group_frame_type != 0)
{
- newptrb.epb_saved_eptr = eptr;
- newptrb.epb_prev = eptrb;
- eptrb = &newptrb;
- mb->match_function_type &= ~MATCH_CBEGROUP;
+ Flast_group_offset = (char *)F - (char *)mb->match_frames;
+ if (GF_IDMASK(group_frame_type) == GF_RECURSE)
+ Fcurrent_recurse = GF_DATAMASK(group_frame_type);
+ group_frame_type = 0;
}
-/* Now, at last, we can start processing the opcodes. */
+
+/* ========================================================================= */
+/* This is the main processing loop. First check that we haven't recorded too
+many backtracks (search tree is too large), or that we haven't exceeded the
+recursive depth limit (used too many backtracking frames). If not, process the
+opcodes. */
+
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
for (;;)
{
- minimize = possessive = FALSE;
- op = *ecode;
+#ifdef DEBUG_SHOW_OPS
+fprintf(stderr, "++ op=%d\n", *Fecode);
+#endif
- switch(op)
+ Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */
+ switch(Fop)
{
- case OP_MARK:
- mb->nomatch_mark = ecode + 2;
- mb->mark = NULL; /* In case previously set by assertion */
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb,
- eptrb, RM55);
- if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) &&
- mb->mark == NULL) mb->mark = ecode + 2;
-
- /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an
- argument, and we must check whether that argument matches this MARK's
- argument. It is passed back in mb->start_match_ptr (an overloading of that
- variable). If it does match, we reset that variable to the current subject
- position and return MATCH_SKIP. Otherwise, pass back the return code
- unaltered. */
+ /* ===================================================================== */
+ /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close
+ any currently open capturing brackets. Unlike reaching the end of a group,
+ where we know the starting frame is at the top of the chained frames, in
+ this case we have to search back for the relevant frame in case other types
+ of group that use chained frames have intervened. Multiple OP_CLOSEs always
+ come innermost first, which matches the chain order. We can ignore this in
+ a recursion, because captures are not passed out of recursions. */
- else if (rrc == MATCH_SKIP_ARG &&
- PRIV(strcmp)(ecode + 2, mb->start_match_ptr) == 0)
+ case OP_CLOSE:
+ if (Fcurrent_recurse == RECURSE_UNSET)
{
- mb->start_match_ptr = eptr;
- RRETURN(MATCH_SKIP);
+ number = GET2(Fecode, 1);
+ offset = Flast_group_offset;
+ for(;;)
+ {
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)mb->match_frames + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_CAPTURE | number)) break;
+ offset = P->last_group_offset;
+ }
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
}
- RRETURN(rrc);
-
- case OP_FAIL:
- RRETURN(MATCH_NOMATCH);
-
- case OP_COMMIT:
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM52);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_COMMIT);
-
- case OP_PRUNE:
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM51);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_PRUNE);
-
- case OP_PRUNE_ARG:
- mb->nomatch_mark = ecode + 2;
- mb->mark = NULL; /* In case previously set by assertion */
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb,
- eptrb, RM56);
- if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) &&
- mb->mark == NULL) mb->mark = ecode + 2;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_PRUNE);
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
- case OP_SKIP:
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM53);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->start_match_ptr = eptr; /* Pass back current position */
- RRETURN(MATCH_SKIP);
- /* Note that, for Perl compatibility, SKIP with an argument does NOT set
- nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was
- not a matching mark, we have to re-run the match, ignoring the SKIP_ARG
- that failed and any that precede it (either they also failed, or were not
- triggered). To do this, we maintain a count of executed SKIP_ARGs. If a
- SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg
- set to the count of the one that failed. */
+ /* ===================================================================== */
+ /* Real or forced end of the pattern, assertion, or recursion. In an
+ assertion ACCEPT, update the last used pointer and remember the current
+ frame so that the captures can be fished out of it. */
- case OP_SKIP_ARG:
- mb->skip_arg_count++;
- if (mb->skip_arg_count <= mb->ignore_skip_arg)
- {
- ecode += PRIV(OP_lengths)[*ecode] + ecode[1];
- break;
- }
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb,
- eptrb, RM57);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ case OP_ASSERT_ACCEPT:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ assert_accept_frame = F;
+ RRETURN(MATCH_ACCEPT);
- /* Pass back the current skip name by overloading mb->start_match_ptr and
- returning the special MATCH_SKIP_ARG return code. This will either be
- caught by a matching MARK, or get to the top, where it causes a rematch
- with mb->ignore_skip_arg set to the value of mb->skip_arg_count. */
+ /* If recursing, we have to find the most recent recursion. */
- mb->start_match_ptr = ecode + 2;
- RRETURN(MATCH_SKIP_ARG);
-
- /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that
- the branch in which it occurs can be determined. Overload the start of
- match pointer to do this. */
-
- case OP_THEN:
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM54);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->start_match_ptr = ecode;
- RRETURN(MATCH_THEN);
+ case OP_ACCEPT:
+ case OP_END:
- case OP_THEN_ARG:
- mb->nomatch_mark = ecode + 2;
- mb->mark = NULL; /* In case previously set by assertion */
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top,
- mb, eptrb, RM58);
- if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) &&
- mb->mark == NULL) mb->mark = ecode + 2;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->start_match_ptr = ecode;
- RRETURN(MATCH_THEN);
+ /* Handle end of a recursion. */
- /* Handle an atomic group that does not contain any capturing parentheses.
- This can be handled like an assertion. Prior to 8.13, all atomic groups
- were handled this way. In 8.13, the code was changed as below for ONCE, so
- that backups pass through the group and thereby reset captured values.
- However, this uses a lot more stack, so in 8.20, atomic groups that do not
- contain any captures generate OP_ONCE_NC, which can be handled in the old,
- less stack intensive way.
-
- Check the alternative branches in turn - the matching won't pass the KET
- for this kind of subpattern. If any one branch matches, we carry on as at
- the end of a normal bracket, leaving the subject pointer, but resetting
- the start-of-match value in case it was changed by \K. */
-
- case OP_ONCE_NC:
- prev = ecode;
- saved_eptr = eptr;
- save_mark = mb->mark;
- do
+ if (Fcurrent_recurse != RECURSE_UNSET)
{
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM64);
- if (rrc == MATCH_MATCH) /* Note: _not_ MATCH_ACCEPT */
+ offset = Flast_group_offset;
+ for(;;)
{
- mstart = mb->start_match_ptr;
- break;
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)mb->match_frames + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break;
+ offset = P->last_group_offset;
}
- if (rrc == MATCH_THEN)
- {
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
- }
-
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += GET(ecode,1);
- mb->mark = save_mark;
- }
- while (*ecode == OP_ALT);
-
- /* If hit the end of the group (which could be repeated), fail */
- if (*ecode != OP_ONCE_NC && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ /* N is now the frame of the recursion; the previous frame is at the
+ OP_RECURSE position. Go back there, copying the current subject position
+ and mark, and move on past the OP_RECURSE. */
- /* Continue as from after the group, updating the offsets high water
- mark, since extracts may have been taken. */
-
- do ecode += GET(ecode, 1); while (*ecode == OP_ALT);
-
- offset_top = mb->end_offset_top;
- eptr = mb->end_match_ptr;
+ P->eptr = Feptr;
+ P->mark = Fmark;
+ F = P;
+ Fecode += 1 + LINK_SIZE;
+ continue;
+ }
- /* For a non-repeating ket, just continue at this level. This also
- happens for a repeating ket if no characters were matched in the group.
- This is the forcible breaking of infinite loops as implemented in Perl
- 5.005. */
+ /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY
+ is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the
+ start of the subject. In both cases, backtracking will then try other
+ alternatives, if any. */
- if (*ecode == OP_KET || eptr == saved_eptr)
- {
- ecode += 1+LINK_SIZE;
- break;
- }
+ if (Feptr == Fstart_match &&
+ ((mb->moptions & PCRE2_NOTEMPTY) != 0 ||
+ ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 &&
+ Fstart_match == mb->start_subject + mb->start_offset)))
+ RRETURN(MATCH_NOMATCH);
- /* The repeating kets try the rest of the pattern or restart from the
- preceding bracket, in the appropriate order. The second "call" of match()
- uses tail recursion, to avoid using another stack frame. */
+ /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not
+ the end of the subject. After (*ACCEPT) we fail the entire match (at this
+ position) but backtrack on reaching the end of the pattern. */
- if (*ecode == OP_KETRMIN)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM65);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode = prev;
- goto TAIL_RECURSE;
- }
- else /* OP_KETRMAX */
+ if (Feptr < mb->end_subject &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0)
{
- RMATCH(eptr, prev, offset_top, mb, eptrb, RM66);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += 1 + LINK_SIZE;
- goto TAIL_RECURSE;
+ if (Fop == OP_END) RRETURN(MATCH_NOMATCH);
+ return MATCH_NOMATCH;
}
- /* Control never gets here */
- /* Handle a capturing bracket, other than those that are possessive with an
- unlimited repeat. If there is space in the offset vector, save the current
- subject position in the working slot at the top of the vector. We mustn't
- change the current values of the data slot, because they may be set from a
- previous iteration of this group, and be referred to by a reference inside
- the group. A failure to match might occur after the group has succeeded,
- if something later on doesn't match. For this reason, we need to restore
- the working value and also the values of the final offsets, in case they
- were set by a previous iteration of the same bracket.
-
- If there isn't enough space in the offset vector, treat this as if it were
- a non-capturing bracket. Don't worry about setting the flag for the error
- case here; that is handled in the code for KET. */
+ /* We have a successful match of the whole pattern. Record the result and
+ then do a direct return from the function. If there is space in the offset
+ vector, set any pairs that follow the highest-numbered captured string but
+ are less than the number of capturing groups in the pattern to PCRE2_UNSET.
+ It is documented that this happens. "Gaps" are set to PCRE2_UNSET
+ dynamically. It is only those at the end that need setting here. */
- case OP_CBRA:
- case OP_SCBRA:
- number = GET2(ecode, 1+LINK_SIZE);
- offset = number << 1;
+ mb->end_match_ptr = Feptr; /* Record where we ended */
+ mb->end_offset_top = Foffset_top; /* and how many extracts were taken */
+ mb->mark = Fmark; /* and the last success mark */
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
- if (offset < mb->offset_max)
- {
- save_offset1 = mb->ovector[offset];
- save_offset2 = mb->ovector[offset+1];
- save_offset3 = mb->ovector[mb->offset_end - number];
- save_capture_last = mb->capture_last;
- save_mark = mb->mark;
+ ovector[0] = Fstart_match - mb->start_subject;
+ ovector[1] = Feptr - mb->start_subject;
- mb->ovector[mb->offset_end - number] = eptr - mb->start_subject;
+ /* Set i to the smaller of the sizes of the external and frame ovectors. */
- for (;;)
- {
- if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP;
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM1);
- if (rrc == MATCH_ONCE) break; /* Backing up through an atomic group */
-
- /* If we backed up to a THEN, check whether it is within the current
- branch by comparing the address of the THEN that is passed back with
- the end of the branch. If it is within the current branch, and the
- branch is one of two or more alternatives (it either starts or ends
- with OP_ALT), we have reached the limit of THEN's action, so convert
- the return code to NOMATCH, which will cause normal backtracking to
- happen from now on. Otherwise, THEN is passed back to an outer
- alternative. This implements Perl's treatment of parenthesized groups,
- where a group not containing | does not affect the current alternative,
- that is, (X) is NOT the same as (X|(*F)). */
-
- if (rrc == MATCH_THEN)
- {
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
- }
+ i = 2 * ((top_bracket + 1 > oveccount)? oveccount : top_bracket + 1);
+ memcpy(ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE));
+ while (--i >= Foffset_top + 2) ovector[i] = PCRE2_UNSET;
+ return MATCH_MATCH; /* Note: NOT RRETURN */
- /* Anything other than NOMATCH is passed back. */
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->capture_last = save_capture_last;
- ecode += GET(ecode, 1);
- mb->mark = save_mark;
- if (*ecode != OP_ALT) break;
- }
-
- mb->ovector[offset] = save_offset1;
- mb->ovector[offset+1] = save_offset2;
- mb->ovector[mb->offset_end - number] = save_offset3;
-
- /* At this point, rrc will be one of MATCH_ONCE or MATCH_NOMATCH. */
+ /*===================================================================== */
+ /* Match any single character type except newline; have to take care with
+ CRLF newlines and partial matching. */
- RRETURN(rrc);
+ case OP_ANY:
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr == mb->end_subject - 1 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
+ /* Fall through */
- /* FALL THROUGH ... Insufficient room for saving captured contents. Treat
- as a non-capturing bracket. */
+ /* Match any single character whatsoever. */
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
+ case OP_ALLANY:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+#ifdef SUPPORT_UNICODE
+ if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+#endif
+ Fecode++;
+ break;
- /* Non-capturing or atomic group, except for possessive with unlimited
- repeat and ONCE group with no captures. Loop for all the alternatives.
- When we get to the final alternative within the brackets, we used to return
- the result of a recursive call to match() whatever happened so it was
- possible to reduce stack usage by turning this into a tail recursion,
- except in the case of a possibly empty group. However, now that there is
- the possiblity of (*THEN) occurring in the final alternative, this
- optimization is no longer always possible.
+ /* ===================================================================== */
+ /* Match a single code unit, even in UTF mode. This opcode really does
+ match any code unit, even newline. (It really should be called ANYCODEUNIT,
+ of course - the byte name is from pre-16 bit days.) */
- We can optimize if we know there are no (*THEN)s in the pattern; at present
- this is the best that can be done.
+ case OP_ANYBYTE:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ Fecode++;
+ break;
- MATCH_ONCE is returned when the end of an atomic group is successfully
- reached, but subsequent matching fails. It passes back up the tree (causing
- captured values to be reset) until the original atomic group level is
- reached. This is tested by comparing mb->once_target with the start of the
- group. At this point, the return is converted into MATCH_NOMATCH so that
- previous backup points can be taken. */
- case OP_ONCE:
- case OP_BRA:
- case OP_SBRA:
+ /* ===================================================================== */
+ /* Match a single character, casefully */
- for (;;)
+ case OP_CHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- if (op >= OP_SBRA || op == OP_ONCE)
- mb->match_function_type |= MATCH_CBEGROUP;
-
- /* If this is not a possibly empty group, and there are no (*THEN)s in
- the pattern, and this is the final alternative, optimize as described
- above. */
-
- else if (!mb->hasthen && ecode[GET(ecode, 1)] != OP_ALT)
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
+ if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr))
{
- ecode += PRIV(OP_lengths)[*ecode];
- goto TAIL_RECURSE;
- }
-
- /* In all other cases, we have to make another call to match(). */
-
- save_mark = mb->mark;
- save_capture_last = mb->capture_last;
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, eptrb,
- RM2);
-
- /* See comment in the code for capturing groups above about handling
- THEN. */
-
- if (rrc == MATCH_THEN)
- {
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
+ CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
}
-
- if (rrc != MATCH_NOMATCH)
+ for (; Flength > 0; Flength--)
{
- if (rrc == MATCH_ONCE)
- {
- PCRE2_SPTR scode = ecode;
- if (*scode != OP_ONCE) /* If not at start, find it */
- {
- while (*scode == OP_ALT) scode += GET(scode, 1);
- scode -= GET(scode, 1);
- }
- if (mb->once_target == scode) rrc = MATCH_NOMATCH;
- }
- RRETURN(rrc);
+ if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH);
}
- ecode += GET(ecode, 1);
- mb->mark = save_mark;
- if (*ecode != OP_ALT) break;
- mb->capture_last = save_capture_last;
}
-
- RRETURN(MATCH_NOMATCH);
-
- /* Handle possessive capturing brackets with an unlimited repeat. We come
- here from BRAZERO with allow_zero set TRUE. The ovector values are
- handled similarly to the normal case above. However, the matching is
- different. The end of these brackets will always be OP_KETRPOS, which
- returns MATCH_KETRPOS without going further in the pattern. By this means
- we can handle the group by iteration rather than recursion, thereby
- reducing the amount of stack needed. If the ovector is too small for
- capturing, treat as non-capturing. */
-
- case OP_CBRAPOS:
- case OP_SCBRAPOS:
- allow_zero = FALSE;
-
- POSSESSIVE_CAPTURE:
- number = GET2(ecode, 1+LINK_SIZE);
- offset = number << 1;
- if (offset >= mb->offset_max) goto POSSESSIVE_NON_CAPTURE;
-
- matched_once = FALSE;
- code_offset = (int)(ecode - mb->start_code);
-
- save_offset1 = mb->ovector[offset];
- save_offset2 = mb->ovector[offset+1];
- save_offset3 = mb->ovector[mb->offset_end - number];
- save_capture_last = mb->capture_last;
-
- /* Each time round the loop, save the current subject position for use
- when the group matches. For MATCH_MATCH, the group has matched, so we
- restart it with a new subject starting position, remembering that we had
- at least one match. For MATCH_NOMATCH, carry on with the alternatives, as
- usual. If we haven't matched any alternatives in any iteration, check to
- see if a previous iteration matched. If so, the group has matched;
- continue from afterwards. Otherwise it has failed; restore the previous
- capture values before returning NOMATCH. */
-
- for (;;)
+ else
+#endif
+ /* Not UTF mode */
{
- mb->ovector[mb->offset_end - number] = eptr - mb->start_subject;
- if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP;
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM63);
- if (rrc == MATCH_KETRPOS)
+ if (mb->end_subject - Feptr < 1)
{
- offset_top = mb->end_offset_top;
- ecode = mb->start_code + code_offset;
- save_capture_last = mb->capture_last;
- matched_once = TRUE;
- mstart = mb->start_match_ptr; /* In case \K changed it */
- if (eptr == mb->end_match_ptr) /* Matched an empty string */
- {
- do ecode += GET(ecode, 1); while (*ecode == OP_ALT);
- break;
- }
- eptr = mb->end_match_ptr;
- continue;
+ SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
}
+ if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
+ }
+ break;
- /* See comment in the code for capturing groups above about handling
- THEN. */
-
- if (rrc == MATCH_THEN)
- {
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
- }
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->capture_last = save_capture_last;
- ecode += GET(ecode, 1);
- if (*ecode != OP_ALT) break;
- }
+ /* ===================================================================== */
+ /* Match a single character, caselessly. If we are at the end of the
+ subject, give up immediately. We get here only when the pattern character
+ has at most one other case. Characters with more than two cases are coded
+ as OP_PROP with the pseudo-property PT_CLIST. */
- if (!matched_once)
+ case OP_CHARI:
+ if (Feptr >= mb->end_subject)
{
- mb->ovector[offset] = save_offset1;
- mb->ovector[offset+1] = save_offset2;
- mb->ovector[mb->offset_end - number] = save_offset3;
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- if (allow_zero || matched_once)
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- ecode += 1 + LINK_SIZE;
- break;
- }
- RRETURN(MATCH_NOMATCH);
-
- /* Non-capturing possessive bracket with unlimited repeat. We come here
- from BRAZERO with allow_zero = TRUE. The code is similar to the above,
- without the capturing complication. It is written out separately for speed
- and cleanliness. */
-
- case OP_BRAPOS:
- case OP_SBRAPOS:
- allow_zero = FALSE;
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
- POSSESSIVE_NON_CAPTURE:
- matched_once = FALSE;
- code_offset = (int)(ecode - mb->start_code);
- save_capture_last = mb->capture_last;
+ /* If the pattern character's value is < 128, we know that its other case
+ (if any) is also < 128 (and therefore only one code unit long in all
+ code-unit widths), so we can use the fast lookup table. We checked above
+ that there is at least one character left in the subject. */
- for (;;)
- {
- if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP;
- RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb,
- eptrb, RM48);
- if (rrc == MATCH_KETRPOS)
+ if (fc < 128)
{
- offset_top = mb->end_offset_top;
- ecode = mb->start_code + code_offset;
- matched_once = TRUE;
- mstart = mb->start_match_ptr; /* In case \K reset it */
- if (eptr == mb->end_match_ptr) /* Matched an empty string */
- {
- do ecode += GET(ecode, 1); while (*ecode == OP_ALT);
- break;
- }
- eptr = mb->end_match_ptr;
- continue;
+ uint32_t cc = UCHAR21(Feptr);
+ if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ Feptr++;
}
- /* See comment in the code for capturing groups above about handling
- THEN. */
+ /* Otherwise we must pick up the subject character and use Unicode
+ property support to test its other case. Note that we cannot use the
+ value of "Flength" to check for sufficient bytes left, because the other
+ case of the character may have more or fewer code units. */
- if (rrc == MATCH_THEN)
+ else
{
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
+ uint32_t dc;
+ GETCHARINC(dc, Feptr);
+ Fecode += Flength;
+ if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);
}
-
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += GET(ecode, 1);
- if (*ecode != OP_ALT) break;
- mb->capture_last = save_capture_last;
}
+ else
+#endif /* SUPPORT_UNICODE */
- if (matched_once || allow_zero)
+ /* Not UTF mode; use the table for characters < 256. */
{
- ecode += 1 + LINK_SIZE;
- break;
+ if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1])
+ != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ Fecode += 2;
}
- RRETURN(MATCH_NOMATCH);
-
- /* Control never reaches here. */
-
- /* Conditional group: compilation checked that there are no more than two
- branches. If the condition is false, skipping the first branch takes us
- past the end of the item if there is only one branch, but that's exactly
- what we want. */
-
- case OP_COND:
- case OP_SCOND:
-
- /* The variable codelink will be added to ecode when the condition is
- false, to get to the second branch. Setting it to the offset to the ALT
- or KET, then incrementing ecode achieves this effect. We now have ecode
- pointing to the condition or callout. */
+ break;
- codelink = GET(ecode, 1); /* Offset to the second branch */
- ecode += 1 + LINK_SIZE; /* From this opcode */
- /* Because of the way auto-callout works during compile, a callout item is
- inserted between OP_COND and an assertion condition. */
+ /* ===================================================================== */
+ /* Match not a single character. */
- if (*ecode == OP_CALLOUT || *ecode == OP_CALLOUT_STR)
+ case OP_NOT:
+ case OP_NOTI:
+ if (Feptr >= mb->end_subject)
{
- unsigned int callout_length = (*ecode == OP_CALLOUT)
- ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE);
-
- if (mb->callout != NULL)
- {
- pcre2_callout_block cb;
- cb.version = 1;
- cb.capture_top = (uint32_t)offset_top/2;
- cb.capture_last = mb->capture_last & CAPLMASK;
- cb.offset_vector = mb->ovector;
- cb.mark = mb->nomatch_mark;
- cb.subject = mb->start_subject;
- cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject);
- cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject);
- cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject);
- cb.pattern_position = GET(ecode, 1);
- cb.next_item_length = GET(ecode, 1 + LINK_SIZE);
-
- if (*ecode == OP_CALLOUT)
- {
- cb.callout_number = ecode[1 + 2*LINK_SIZE];
- cb.callout_string_offset = 0;
- cb.callout_string = NULL;
- cb.callout_string_length = 0;
- }
- else
- {
- cb.callout_number = 0;
- cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE);
- cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1;
- cb.callout_string_length =
- callout_length - (1 + 4*LINK_SIZE) - 2;
- }
-
- if ((rrc = mb->callout(&cb, mb->callout_data)) > 0)
- RRETURN(MATCH_NOMATCH);
- if (rrc < 0) RRETURN(rrc);
- }
-
- /* Advance ecode past the callout, so it now points to the condition. We
- must adjust codelink so that the value of ecode+codelink is unchanged. */
-
- ecode += callout_length;
- codelink -= callout_length;
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
-
- /* Test the various possible conditions */
-
- condition = FALSE;
- switch(condcode = *ecode)
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- case OP_RREF: /* Numbered group recursion test */
- if (mb->recursive != NULL) /* Not recursing => FALSE */
- {
- uint32_t recno = GET2(ecode, 1); /* Recursion group number*/
- condition = (recno == RREF_ANY || recno == mb->recursive->group_num);
- }
- break;
-
- case OP_DNRREF: /* Duplicate named group recursion test */
- if (mb->recursive != NULL)
- {
- int count = GET2(ecode, 1 + IMM2_SIZE);
- PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size;
- while (count-- > 0)
- {
- uint32_t recno = GET2(slot, 0);
- condition = recno == mb->recursive->group_num;
- if (condition) break;
- slot += mb->name_entry_size;
- }
- }
- break;
-
- case OP_CREF: /* Numbered group used test */
- offset = GET2(ecode, 1) << 1; /* Doubled ref number */
- condition = offset < offset_top &&
- mb->ovector[offset] != PCRE2_UNSET;
- break;
-
- case OP_DNCREF: /* Duplicate named group used test */
- {
- int count = GET2(ecode, 1 + IMM2_SIZE);
- PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size;
- while (count-- > 0)
- {
- offset = GET2(slot, 0) << 1;
- condition = offset < offset_top &&
- mb->ovector[offset] != PCRE2_UNSET;
- if (condition) break;
- slot += mb->name_entry_size;
- }
- }
- break;
-
- case OP_FALSE:
- case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */
- break;
-
- case OP_TRUE:
- condition = TRUE;
- break;
-
- /* The condition is an assertion. Call match() to evaluate it - setting
- the MATCH_CONDASSERT bit in mb->match_function_type causes it to stop at
- the end of an assertion. */
-
- default:
- mb->match_function_type |= MATCH_CONDASSERT;
- RMATCH(eptr, ecode, offset_top, mb, NULL, RM3);
- if (rrc == MATCH_MATCH)
- {
- if (mb->end_offset_top > offset_top)
- offset_top = mb->end_offset_top; /* Captures may have happened */
- condition = TRUE;
-
- /* Advance ecode past the assertion to the start of the first branch,
- but adjust it so that the general choosing code below works. If the
- assertion has a quantifier that allows zero repeats we must skip over
- the BRAZERO. This is a lunatic thing to do, but somebody did! */
-
- if (*ecode == OP_BRAZERO) ecode++;
- ecode += GET(ecode, 1);
- while (*ecode == OP_ALT) ecode += GET(ecode, 1);
- ecode += 1 + LINK_SIZE - PRIV(OP_lengths)[condcode];
- }
-
- /* PCRE doesn't allow the effect of (*THEN) to escape beyond an
- assertion; it is therefore treated as NOMATCH. Any other return is an
- error. */
-
- else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN)
+ uint32_t ch;
+ Fecode++;
+ GETCHARINC(ch, Fecode);
+ GETCHARINC(fc, Feptr);
+ if (ch == fc)
{
- RRETURN(rrc); /* Need braces because of following else */
+ RRETURN(MATCH_NOMATCH); /* Caseful match */
}
- break;
- }
-
- /* Choose branch according to the condition */
-
- ecode += condition? PRIV(OP_lengths)[condcode] : codelink;
-
- /* We are now at the branch that is to be obeyed. As there is only one, we
- can use tail recursion to avoid using another stack frame, except when
- there is unlimited repeat of a possibly empty group. In the latter case, a
- recursive call to match() is always required, unless the second alternative
- doesn't exist, in which case we can just plough on. Note that, for
- compatibility with Perl, the | in a conditional group is NOT treated as
- creating two alternatives. If a THEN is encountered in the branch, it
- propagates out to the enclosing alternative (unless nested in a deeper set
- of alternatives, of course). */
-
- if (condition || ecode[-(1+LINK_SIZE)] == OP_ALT)
- {
- if (op != OP_SCOND)
+ else if (Fop == OP_NOTI) /* If caseless */
{
- goto TAIL_RECURSE;
+ if (ch > 127)
+ ch = UCD_OTHERCASE(ch);
+ else
+ ch = TABLE_GET(ch, mb->fcc, ch);
+ if (ch == fc) RRETURN(MATCH_NOMATCH);
}
-
- mb->match_function_type |= MATCH_CBEGROUP;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM49);
- RRETURN(rrc);
}
-
- /* Condition false & no alternative; continue after the group. */
-
else
+#endif /* SUPPORT_UNICODE */
{
+ uint32_t ch = Fecode[1];
+ fc = *Feptr++;
+ if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc))
+ RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
}
break;
- /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes,
- to close any currently open capturing brackets. */
-
- case OP_CLOSE:
- number = GET2(ecode, 1); /* Must be less than 65536 */
- offset = number << 1;
- mb->capture_last = (mb->capture_last & OVFLMASK) | number;
- if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else
- {
- mb->ovector[offset] =
- mb->ovector[mb->offset_end - number];
- mb->ovector[offset+1] = eptr - mb->start_subject;
-
- /* If this group is at or above the current highwater mark, ensure that
- any groups between the current high water mark and this group are marked
- unset and then update the high water mark. */
-
- if (offset >= offset_top)
- {
- PCRE2_SIZE *iptr = mb->ovector + offset_top;
- PCRE2_SIZE *iend = mb->ovector + offset;
- while (iptr < iend) *iptr++ = PCRE2_UNSET;
- offset_top = offset + 2;
- }
- }
- ecode += 1 + IMM2_SIZE;
- break;
-
+ /* ===================================================================== */
+ /* Match a single character repeatedly. */
- /* End of the pattern, either real or forced. In an assertion ACCEPT,
- update the last used pointer. */
+#define Loclength F->temp_size
+#define Lstart_eptr F->temp_sptr[0]
+#define Lcharptr F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
- case OP_ASSERT_ACCEPT:
- if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr;
+ case OP_EXACT:
+ case OP_EXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
- case OP_ACCEPT:
- case OP_END:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
- /* If we have matched an empty string, fail if not in an assertion and not
- in a recursion if either PCRE2_NOTEMPTY is set, or if PCRE2_NOTEMPTY_ATSTART
- is set and we have matched at the start of the subject. In both cases,
- backtracking will then try other alternatives, if any. */
+ case OP_UPTO:
+ case OP_UPTOI:
+ reptype = REPTYPE_MAX;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
- if (eptr == mstart && op != OP_ASSERT_ACCEPT &&
- mb->recursive == NULL &&
- ((mb->moptions & PCRE2_NOTEMPTY) != 0 ||
- ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 &&
- mstart == mb->start_subject + mb->start_offset)))
- RRETURN(MATCH_NOMATCH);
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ reptype = REPTYPE_MIN;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
- /* Otherwise, we have a match. */
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
- mb->end_match_ptr = eptr; /* Record where we ended */
- mb->end_offset_top = offset_top; /* and how many extracts were taken */
- mb->start_match_ptr = mstart; /* and the start (\K can modify) */
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
- /* For some reason, the macros don't work properly if an expression is
- given as the argument to RRETURN when the heap is in use. */
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATCHAR;
- rrc = (op == OP_END)? MATCH_MATCH : MATCH_ACCEPT;
- RRETURN(rrc);
+ case OP_STAR:
+ case OP_STARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
- /* Assertion brackets. Check the alternative branches in turn - the
- matching won't pass the KET for an assertion. If any one branch matches,
- the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
- start of each branch to move the current point backwards, so the code at
- this level is identical to the lookahead case. When the assertion is part
- of a condition, we want to return immediately afterwards. The caller of
- this incarnation of the match() function will have set MATCH_CONDASSERT in
- mb->match_function type, and one of these opcodes will be the first opcode
- that is processed. We use a local variable that is preserved over calls to
- match() to remember this case. */
+ /* Common code for all repeated single-character matches. We first check
+ for the minimum number of characters. If the minimum equals the maximum, we
+ are done. Otherwise, if minimizing, check the rest of the pattern for a
+ match; if there isn't one, advance up to the maximum, one character at a
+ time.
- case OP_ASSERT:
- case OP_ASSERTBACK:
- save_mark = mb->mark;
- if ((mb->match_function_type & MATCH_CONDASSERT) != 0)
- {
- condassert = TRUE;
- mb->match_function_type &= ~MATCH_CONDASSERT;
- }
- else condassert = FALSE;
+ If maximizing, advance up to the maximum number of matching characters,
+ until Feptr is past the end of the maximum run. If possessive, we are
+ then done (no backing up). Otherwise, match at this position; anything
+ other than no match is immediately returned. For nomatch, back up one
+ character, unless we are matching \R and the last thing matched was
+ \r\n, in which case, back up two code units until we reach the first
+ optional character position.
- /* Loop for each branch */
+ The various UTF/non-UTF and caseful/caseless cases are handled separately,
+ for speed. */
- do
+ REPEATCHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM4);
+ Flength = 1;
+ Lcharptr = Fecode;
+ GETCHARLEN(fc, Fecode, Flength);
+ Fecode += Flength;
- /* A match means that the assertion is true; break out of the loop
- that matches its alternatives. */
+ /* Handle multi-code-unit character matching, caseful and caseless. */
- if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT)
+ if (Flength > 1)
{
- mstart = mb->start_match_ptr; /* In case \K reset it */
- break;
- }
-
- /* If not matched, restore the previous mark setting. */
-
- mb->mark = save_mark;
-
- /* See comment in the code for capturing groups above about handling
- THEN. */
+ uint32_t othercase;
- if (rrc == MATCH_THEN)
- {
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
- rrc = MATCH_NOMATCH;
- }
+ if (Fop >= OP_STARI && /* Caseless */
+ (othercase = UCD_OTHERCASE(fc)) != fc)
+ Loclength = PRIV(ord2utf)(othercase, Foccu);
+ else Loclength = 0;
- /* Anything other than NOMATCH causes the entire assertion to fail,
- passing back the return code. This includes COMMIT, SKIP, PRUNE and an
- uncaptured THEN, which means they take their normal effect. This
- consistent approach does not always have exactly the same effect as in
- Perl. */
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += GET(ecode, 1);
- }
- while (*ecode == OP_ALT); /* Continue for next alternative */
+ if (Lmin == Lmax) continue;
- /* If we have tried all the alternative branches, the assertion has
- failed. If not, we broke out after a match. */
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM202);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
- if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH);
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0)
+ Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ break;
+ }
+ }
- /* If checking an assertion for a condition, return MATCH_MATCH. */
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
- if (condassert) RRETURN(MATCH_MATCH);
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM203);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ break; /* End of repeated wide character handling */
+ }
- /* Continue from after a successful assertion, updating the offsets high
- water mark, since extracts may have been taken during the assertion. */
+ /* Length of UTF character is 1. Put it into the preserved variable and
+ fall through to the non-UTF code. */
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- ecode += 1 + LINK_SIZE;
- offset_top = mb->end_offset_top;
- continue;
+ Lc = fc;
+ }
+ else
+#endif /* SUPPORT_UNICODE */
- /* Negative assertion: all branches must fail to match for the assertion to
- succeed. */
+ /* When not in UTF mode, load a single-code-unit character. Then proceed as
+ above. */
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK_NOT:
- save_mark = mb->mark;
- if ((mb->match_function_type & MATCH_CONDASSERT) != 0)
- {
- condassert = TRUE;
- mb->match_function_type &= ~MATCH_CONDASSERT;
- }
- else condassert = FALSE;
+ Lc = *Fecode++;
- /* Loop for each alternative branch. */
+ /* Caseless comparison */
- do
+ if (Fop >= OP_STARI)
{
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM5);
- mb->mark = save_mark; /* Always restore the mark setting */
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ /* Lc must be < 128 in UTF-8 mode. */
+ Loc = mb->fcc[Lc];
+#else /* 16-bit & 32-bit */
+#ifdef SUPPORT_UNICODE
+ if (utf && Lc > 127) Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+ Loc = TABLE_GET(Lc, mb->fcc, Lc);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
- switch(rrc)
+ for (i = 1; i <= Lmin; i++)
{
- case MATCH_MATCH: /* A successful match means */
- case MATCH_ACCEPT: /* the assertion has failed. */
- RRETURN(MATCH_NOMATCH);
-
- case MATCH_NOMATCH: /* Carry on with next branch */
- break;
-
- /* See comment in the code for capturing groups above about handling
- THEN. */
-
- case MATCH_THEN:
- next_ecode = ecode + GET(ecode,1);
- if (mb->start_match_ptr < next_ecode &&
- (*ecode == OP_ALT || *next_ecode == OP_ALT))
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
{
- rrc = MATCH_NOMATCH;
- break;
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- /* Otherwise fall through. */
-
- /* COMMIT, SKIP, PRUNE, and an uncaptured THEN cause the whole
- assertion to fail to match, without considering any more alternatives.
- Failing to match means the assertion is true. This is a consistent
- approach, but does not always have the same effect as in Perl. */
-
- case MATCH_COMMIT:
- case MATCH_SKIP:
- case MATCH_SKIP_ARG:
- case MATCH_PRUNE:
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- goto NEG_ASSERT_TRUE; /* Break out of alternation loop */
-
- /* Anything else is an error */
-
- default:
- RRETURN(rrc);
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
}
+ if (Lmin == Lmax) continue;
- /* Continue with next branch */
-
- ecode += GET(ecode,1);
- }
- while (*ecode == OP_ALT);
-
- /* All branches in the assertion failed to match. */
-
- NEG_ASSERT_TRUE:
- if (condassert) RRETURN(MATCH_MATCH); /* Condition assertion */
- ecode += 1 + LINK_SIZE; /* Continue with current branch */
- continue;
-
- /* Move the subject pointer back. This occurs only at the start of
- each branch of a lookbehind assertion. If we are too close to the start to
- move back, this match function fails. When working with UTF-8 we move
- back a number of characters, not bytes. */
-
- case OP_REVERSE:
- i = GET(ecode, 1);
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- while (i-- > 0)
+ if (reptype == REPTYPE_MIN)
{
- if (eptr <= mb->start_subject) RRETURN(MATCH_NOMATCH);
- eptr--;
- BACKCHAR(eptr);
+ for (;;)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ RMATCH(Fecode, RM25);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ /* Control never gets here */
}
- }
- else
-#endif
- /* No UTF-8 support, or not in UTF-8 mode: count is byte count */
-
- {
- if (i > eptr - mb->start_subject) RRETURN(MATCH_NOMATCH);
- eptr -= i;
- }
-
- /* Save the earliest consulted character, then skip to next op code */
-
- if (eptr < mb->start_used_ptr) mb->start_used_ptr = eptr;
- ecode += 1 + LINK_SIZE;
- break;
-
- /* The callout item calls an external function, if one is provided, passing
- details of the match so far. This is mainly for debugging, though the
- function is able to force a failure. */
-
- case OP_CALLOUT:
- case OP_CALLOUT_STR:
- {
- unsigned int callout_length = (*ecode == OP_CALLOUT)
- ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE);
-
- if (mb->callout != NULL)
+ else /* Maximize */
{
- pcre2_callout_block cb;
- cb.version = 1;
- cb.callout_number = ecode[LINK_SIZE + 1];
- cb.capture_top = (uint32_t)offset_top/2;
- cb.capture_last = mb->capture_last & CAPLMASK;
- cb.offset_vector = mb->ovector;
- cb.mark = mb->nomatch_mark;
- cb.subject = mb->start_subject;
- cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject);
- cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject);
- cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject);
- cb.pattern_position = GET(ecode, 1);
- cb.next_item_length = GET(ecode, 1 + LINK_SIZE);
-
- if (*ecode == OP_CALLOUT)
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
{
- cb.callout_number = ecode[1 + 2*LINK_SIZE];
- cb.callout_string_offset = 0;
- cb.callout_string = NULL;
- cb.callout_string_length = 0;
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) break;
+ Feptr++;
}
- else
+ if (reptype != REPTYPE_POS) for (;;)
{
- cb.callout_number = 0;
- cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE);
- cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1;
- cb.callout_string_length =
- callout_length - (1 + 4*LINK_SIZE) - 2;
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM26);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
}
-
- if ((rrc = mb->callout(&cb, mb->callout_data)) > 0)
- RRETURN(MATCH_NOMATCH);
- if (rrc < 0) RRETURN(rrc);
}
- ecode += callout_length;
}
- break;
-
- /* Recursion either matches the current regex, or some subexpression. The
- offset data is the offset to the starting bracket from the start of the
- whole pattern. (This is so that it works from duplicated subpatterns.)
-
- The state of the capturing groups is preserved over recursion, and
- re-instated afterwards. We don't know how many are started and not yet
- finished (offset_top records the completed total) so we just have to save
- all the potential data. There may be up to 65535 such values, which is too
- large to put on the stack, but using malloc for small numbers seems
- expensive. As a compromise, the stack is used when there are no more than
- OP_RECURSE_STACK_SAVE_MAX values to store; otherwise malloc is used.
- There are also other values that have to be saved. We use a chained
- sequence of blocks that actually live on the stack. Thanks to Robin Houston
- for the original version of this logic. It has, however, been hacked around
- a lot, so he is not to blame for the current way it works. */
+ /* Caseful comparisons (includes all multi-byte characters) */
- case OP_RECURSE:
+ else
{
- ovecsave_frame *fr;
- recursion_info *ri;
- uint32_t recno;
-
- callpat = mb->start_code + GET(ecode, 1);
- recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE);
-
- /* Check for repeating a pattern recursion without advancing the subject
- pointer. This should catch convoluted mutual recursions. (Some simple
- cases are caught at compile time.) */
-
- for (ri = mb->recursive; ri != NULL; ri = ri->prevrec)
- if (recno == ri->group_num && eptr == ri->subject_position)
- RRETURN(PCRE2_ERROR_RECURSELOOP);
-
- /* Add to "recursing stack" */
-
- new_recursive.group_num = recno;
- new_recursive.saved_capture_last = mb->capture_last;
- new_recursive.subject_position = eptr;
- new_recursive.prevrec = mb->recursive;
- mb->recursive = &new_recursive;
-
- /* Where to continue from afterwards */
-
- ecode += 1 + LINK_SIZE;
-
- /* When we are using the system stack for match() recursion we can call a
- function that uses the system stack for preserving the ovector while
- processing the pattern recursion, but only if the ovector is small
- enough. */
-
-#ifndef HEAP_MATCH_RECURSE
- if (mb->offset_end <= OP_RECURSE_STACK_SAVE_MAX)
- {
- rrc = op_recurse_ovecsave(eptr, callpat, mstart, offset_top, mb,
- eptrb, rdepth);
- mb->recursive = new_recursive.prevrec;
- if (rrc != MATCH_MATCH && rrc != MATCH_ACCEPT) RRETURN(rrc);
-
- /* Set where we got to in the subject, and reset the start, in case
- it was changed by \K. This *is* propagated back out of a recursion,
- for Perl compatibility. */
-
- eptr = mb->end_match_ptr;
- mstart = mb->start_match_ptr;
- break; /* End of processing OP_RECURSE */
- }
-#endif
- /* If the ovector is too big, or if we are using the heap for match()
- recursion, we have to use the heap for saving the ovector. Used ovecsave
- frames are kept on a chain and re-used. This makes a small improvement in
- execution time on Linux. */
-
- if (mb->ovecsave_chain != NULL)
- {
- new_recursive.ovec_save = mb->ovecsave_chain->saved_ovec;
- mb->ovecsave_chain = mb->ovecsave_chain->next;
- }
- else
+ for (i = 1; i <= Lmin; i++)
{
- fr = (ovecsave_frame *)(mb->memctl.malloc(sizeof(ovecsave_frame *) +
- mb->offset_end * sizeof(PCRE2_SIZE), mb->memctl.memory_data));
- if (fr == NULL) RRETURN(PCRE2_ERROR_NOMEMORY);
- new_recursive.ovec_save = fr->saved_ovec;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
}
- memcpy(new_recursive.ovec_save, mb->ovector,
- mb->offset_end * sizeof(PCRE2_SIZE));
+ if (Lmin == Lmax) continue;
- /* Do the recursion. After processing each alternative, restore the
- ovector data and the last captured value. This code has the same overall
- logic as the code in the op_recurse_ovecsave() function, but is adapted
- to use RMATCH/RRETURN and to release the heap block containing the saved
- ovector. */
-
- cbegroup = (*callpat >= OP_SBRA);
- do
+ if (reptype == REPTYPE_MIN)
{
- if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP;
- RMATCH(eptr, callpat + PRIV(OP_lengths)[*callpat], offset_top,
- mb, eptrb, RM6);
- memcpy(mb->ovector, new_recursive.ovec_save,
- mb->offset_end * sizeof(PCRE2_SIZE));
- mb->capture_last = new_recursive.saved_capture_last;
- mb->recursive = new_recursive.prevrec;
-
- if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT)
+ for (;;)
{
- fr = (ovecsave_frame *)
- ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *));
- fr->next = mb->ovecsave_chain;
- mb->ovecsave_chain = fr;
-
- /* Set where we got to in the subject, and reset the start, in case
- it was changed by \K. This *is* propagated back out of a recursion,
- for Perl compatibility. */
-
- eptr = mb->end_match_ptr;
- mstart = mb->start_match_ptr;
- goto RECURSION_MATCHED; /* Exit loop; end processing */
+ RMATCH(Fecode, RM27);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
}
+ /* Control never gets here */
+ }
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
- /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a
- recursion; they cause a NOMATCH for the entire recursion. These codes
- are defined in a range that can be tested for. */
+ if (Lc != UCHAR21TEST(Feptr)) break;
+ Feptr++;
+ }
- if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX)
+ if (reptype != REPTYPE_POS) for (;;)
{
- rrc = MATCH_NOMATCH;
- goto RECURSION_RETURN;
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM28);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
}
-
- /* Any return code other than NOMATCH is an error. */
-
- if (rrc != MATCH_NOMATCH) goto RECURSION_RETURN;
- mb->recursive = &new_recursive;
- callpat += GET(callpat, 1);
}
- while (*callpat == OP_ALT);
-
- RECURSION_RETURN:
- mb->recursive = new_recursive.prevrec;
- fr = (ovecsave_frame *)
- ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *));
- fr->next = mb->ovecsave_chain;
- mb->ovecsave_chain = fr;
- RRETURN(rrc);
}
-
- RECURSION_MATCHED:
break;
- /* An alternation is the end of a branch; scan along to find the end of the
- bracketed group and go to there. */
+#undef Loclength
+#undef Lstart_eptr
+#undef Lcharptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
- case OP_ALT:
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- break;
- /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group,
- indicating that it may occur zero times. It may repeat infinitely, or not
- at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets
- with fixed upper repeat limits are compiled as a number of copies, with the
- optional ones preceded by BRAZERO or BRAMINZERO. */
+ /* ===================================================================== */
+ /* Match a negated single one-byte character repeatedly. This is almost a
+ repeat of the code for a repeated single character, but I haven't found a
+ nice way of commoning these up that doesn't require a test of the
+ positive/negative option for each character match. Maybe that wouldn't add
+ very much to the time taken, but character matching *is* what this is all
+ about... */
- case OP_BRAZERO:
- next_ecode = ecode + 1;
- RMATCH(eptr, next_ecode, offset_top, mb, eptrb, RM10);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT);
- ecode = next_ecode + 1 + LINK_SIZE;
- break;
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
- case OP_BRAMINZERO:
- next_ecode = ecode + 1;
- do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT);
- RMATCH(eptr, next_ecode + 1+LINK_SIZE, offset_top, mb, eptrb, RM11);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode++;
- break;
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
- case OP_SKIPZERO:
- next_ecode = ecode+1;
- do next_ecode += GET(next_ecode,1); while (*next_ecode == OP_ALT);
- ecode = next_ecode + 1 + LINK_SIZE;
- break;
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
- /* BRAPOSZERO occurs before a possessive bracket group. Don't do anything
- here; just jump to the group, with allow_zero set TRUE. */
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MIN;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
- case OP_BRAPOSZERO:
- op = *(++ecode);
- allow_zero = TRUE;
- if (op == OP_CBRAPOS || op == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE;
- goto POSSESSIVE_NON_CAPTURE;
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
- /* End of a group, repeated or non-repeating. */
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
- case OP_KET:
- case OP_KETRMIN:
- case OP_KETRMAX:
- case OP_KETRPOS:
- prev = ecode - GET(ecode, 1);
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATNOTCHAR;
- /* If this was a group that remembered the subject start, in order to break
- infinite repeats of empty string matches, retrieve the subject start from
- the chain. Otherwise, set it NULL. */
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
- if (*prev >= OP_SBRA || *prev == OP_ONCE)
- {
- saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */
- eptrb = eptrb->epb_prev; /* Backup to previous group */
- }
- else saved_eptr = NULL;
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
- /* If we are at the end of an assertion group or a non-capturing atomic
- group, stop matching and return MATCH_MATCH, but record the current high
- water mark for use by positive assertions. We also need to record the match
- start in case it was changed by \K. */
+ /* Common code for all repeated single-character non-matches. */
- if ((*prev >= OP_ASSERT && *prev <= OP_ASSERTBACK_NOT) ||
- *prev == OP_ONCE_NC)
- {
- mb->end_match_ptr = eptr; /* For ONCE_NC */
- mb->end_offset_top = offset_top;
- mb->start_match_ptr = mstart;
- if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr;
- RRETURN(MATCH_MATCH); /* Sets mb->mark */
- }
+ REPEATNOTCHAR:
+ GETCHARINCTEST(Lc, Fecode);
- /* For capturing groups we have to check the group number back at the start
- and if necessary complete handling an extraction by setting the offsets and
- bumping the high water mark. Whole-pattern recursion is coded as a recurse
- into group 0, so it won't be picked up here. Instead, we catch it when the
- OP_END is reached. Other recursion is handled here. We just have to record
- the current subject position and start match pointer and give a MATCH
- return. */
+ /* The code is duplicated for the caseless and caseful cases, for speed,
+ since matching characters is likely to be quite common. First, ensure the
+ minimum number of matches are present. If Lmin = Lmax, we are done.
+ Otherwise, if minimizing, keep trying the rest of the expression and
+ advancing one matching character if failing, up to the maximum.
+ Alternatively, if maximizing, find the maximum number of characters and
+ work backwards. */
- if (*prev == OP_CBRA || *prev == OP_SCBRA ||
- *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS)
+ if (Fop >= OP_NOTSTARI) /* Caseless */
{
- number = GET2(prev, 1+LINK_SIZE);
- offset = number << 1;
-
- /* Handle a recursively called group. */
-
- if (mb->recursive != NULL && mb->recursive->group_num == number)
- {
- mb->end_match_ptr = eptr;
- mb->start_match_ptr = mstart;
- if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr;
- RRETURN(MATCH_MATCH);
- }
+#ifdef SUPPORT_UNICODE
+ if (utf && Lc > 127)
+ Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
- /* Deal with capturing */
+ Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */
- mb->capture_last = (mb->capture_last & OVFLMASK) | number;
- if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- /* If offset is greater than offset_top, it means that we are
- "skipping" a capturing group, and that group's offsets must be marked
- unset. In earlier versions of PCRE, all the offsets were unset at the
- start of matching, but this doesn't work because atomic groups and
- assertions can cause a value to be set that should later be unset.
- Example: matching /(?>(a))b|(a)c/ against "ac". This sets group 1 as
- part of the atomic group, but this is not on the final matching path,
- so must be unset when 2 is set. (If there is no group 2, there is no
- problem, because offset_top will then be 2, indicating no capture.) */
-
- if (offset > offset_top)
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
{
- PCRE2_SIZE *iptr = mb->ovector + offset_top;
- PCRE2_SIZE *iend = mb->ovector + offset;
- while (iptr < iend) *iptr++ = PCRE2_UNSET;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
}
-
- /* Now make the extraction */
-
- mb->ovector[offset] = mb->ovector[mb->offset_end - number];
- mb->ovector[offset+1] = eptr - mb->start_subject;
- if (offset_top <= offset) offset_top = offset + 2;
- }
- }
-
- /* OP_KETRPOS is a possessive repeating ket. Remember the current position,
- and return the MATCH_KETRPOS. This makes it possible to do the repeats one
- at a time from the outer level, thus saving stack. This must precede the
- empty string test - in this case that test is done at the outer level. */
-
- if (*ecode == OP_KETRPOS)
- {
- mb->start_match_ptr = mstart; /* In case \K reset it */
- mb->end_match_ptr = eptr;
- mb->end_offset_top = offset_top;
- if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr;
- RRETURN(MATCH_KETRPOS);
- }
-
- /* For an ordinary non-repeating ket, just continue at this level. This
- also happens for a repeating ket if no characters were matched in the
- group. This is the forcible breaking of infinite loops as implemented in
- Perl 5.005. For a non-repeating atomic group that includes captures,
- establish a backup point by processing the rest of the pattern at a lower
- level. If this results in a NOMATCH return, pass MATCH_ONCE back to the
- original OP_ONCE level, thereby bypassing intermediate backup points, but
- resetting any captures that happened along the way. */
-
- if (*ecode == OP_KET || eptr == saved_eptr)
- {
- if (*prev == OP_ONCE)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM12);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */
- RRETURN(MATCH_ONCE);
- }
- ecode += 1 + LINK_SIZE; /* Carry on at this level */
- break;
- }
-
- /* The normal repeating kets try the rest of the pattern or restart from
- the preceding bracket, in the appropriate order. In the second case, we can
- use tail recursion to avoid using another stack frame, unless we have an
- an atomic group or an unlimited repeat of a group that can match an empty
- string. */
-
- if (*ecode == OP_KETRMIN)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM7);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (*prev == OP_ONCE)
- {
- RMATCH(eptr, prev, offset_top, mb, eptrb, RM8);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */
- RRETURN(MATCH_ONCE);
- }
- if (*prev >= OP_SBRA) /* Could match an empty string */
- {
- RMATCH(eptr, prev, offset_top, mb, eptrb, RM50);
- RRETURN(rrc);
- }
- ecode = prev;
- goto TAIL_RECURSE;
- }
- else /* OP_KETRMAX */
- {
- RMATCH(eptr, prev, offset_top, mb, eptrb, RM13);
- if (rrc == MATCH_ONCE && mb->once_target == prev) rrc = MATCH_NOMATCH;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (*prev == OP_ONCE)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM9);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- mb->once_target = prev;
- RRETURN(MATCH_ONCE);
}
- ecode += 1 + LINK_SIZE;
- goto TAIL_RECURSE;
- }
- /* Control never gets here */
-
- /* Not multiline mode: start of subject assertion, unless notbol. */
-
- case OP_CIRC:
- if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject)
- RRETURN(MATCH_NOMATCH);
-
- /* Start of subject assertion */
-
- case OP_SOD:
- if (eptr != mb->start_subject) RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Multiline mode: start of subject unless notbol, or after any newline
- except for one at the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */
-
- case OP_CIRCM:
- if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject)
- RRETURN(MATCH_NOMATCH);
- if (eptr != mb->start_subject &&
- ((eptr == mb->end_subject &&
- (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) ||
- !WAS_NEWLINE(eptr)))
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Start of match assertion */
-
- case OP_SOM:
- if (eptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Reset the start of match point */
-
- case OP_SET_SOM:
- mstart = eptr;
- ecode++;
- break;
-
- /* Multiline mode: assert before any newline, or before end of subject
- unless noteol is set. */
+ else
+#endif /* SUPPORT_UNICODE */
- case OP_DOLLM:
- if (eptr < mb->end_subject)
- {
- if (!IS_NEWLINE(eptr))
+ /* Not UTF mode */
{
- if (mb->partial != 0 &&
- eptr + 1 >= mb->end_subject &&
- NLBLOCK->nltype == NLTYPE_FIXED &&
- NLBLOCK->nllen == 2 &&
- UCHAR21TEST(eptr) == NLBLOCK->nl[0])
+ for (i = 1; i <= Lmin; i++)
{
- mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
}
- RRETURN(MATCH_NOMATCH);
}
- }
- else
- {
- if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
- SCHECK_PARTIAL();
- }
- ecode++;
- break;
-
- /* Not multiline mode: assert before a terminating newline or before end of
- subject unless noteol is set. */
-
- case OP_DOLL:
- if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
- if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS;
- /* ... else fall through for endonly */
+ if (Lmin == Lmax) continue; /* Finished for exact count */
- /* End of subject assertion (\z) */
-
- case OP_EOD:
- if (eptr < mb->end_subject) RRETURN(MATCH_NOMATCH);
- SCHECK_PARTIAL();
- ecode++;
- break;
-
- /* End of subject or ending \n assertion (\Z) */
-
- case OP_EODN:
- ASSERT_NL_OR_EOS:
- if (eptr < mb->end_subject &&
- (!IS_NEWLINE(eptr) || eptr != mb->end_subject - mb->nllen))
- {
- if (mb->partial != 0 &&
- eptr + 1 >= mb->end_subject &&
- NLBLOCK->nltype == NLTYPE_FIXED &&
- NLBLOCK->nllen == 2 &&
- UCHAR21TEST(eptr) == NLBLOCK->nl[0])
+ if (reptype == REPTYPE_MIN)
{
- mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
- }
- RRETURN(MATCH_NOMATCH);
- }
-
- /* Either at end of string or \n before end. */
-
- SCHECK_PARTIAL();
- ecode++;
- break;
-
- /* Word boundary assertions */
-
- case OP_NOT_WORD_BOUNDARY:
- case OP_WORD_BOUNDARY:
- {
-
- /* Find out if the previous and current characters are "word" characters.
- It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to
- be "non-word" characters. Remember the earliest consulted character for
- partial matching. */
-
#ifdef SUPPORT_UNICODE
- if (utf)
- {
- /* Get status of previous character */
-
- if (eptr == mb->start_subject) prev_is_word = FALSE; else
+ if (utf)
{
- PCRE2_SPTR lastptr = eptr - 1;
- BACKCHAR(lastptr);
- if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr;
- GETCHAR(c, lastptr);
- if ((mb->poptions & PCRE2_UCP) != 0)
+ uint32_t d;
+ for (;;)
{
- if (c == '_') prev_is_word = TRUE; else
+ RMATCH(Fecode, RM204);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
- int cat = UCD_CATEGORY(c);
- prev_is_word = (cat == ucp_L || cat == ucp_N);
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
}
- else
- prev_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0;
- }
-
- /* Get status of next character */
-
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- cur_is_word = FALSE;
}
else
+#endif /*SUPPORT_UNICODE */
+
+ /* Not UTF mode */
{
- PCRE2_SPTR nextptr = eptr + 1;
- FORWARDCHARTEST(nextptr, mb->end_subject);
- if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr;
- GETCHAR(c, eptr);
- if ((mb->poptions & PCRE2_UCP) != 0)
+ for (;;)
{
- if (c == '_') cur_is_word = TRUE; else
+ RMATCH(Fecode, RM29);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
- int cat = UCD_CATEGORY(c);
- cur_is_word = (cat == ucp_L || cat == ucp_N);
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
}
- else
- cur_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0;
}
+ /* Control never gets here */
}
- else
-#endif /* SUPPORT UTF */
- /* Not in UTF-8 mode, but we may still have PCRE2_UCP set, and for
- consistency with the behaviour of \w we do use it in this case. */
+ /* Maximize case */
+ else
{
- /* Get status of previous character */
+ Lstart_eptr = Feptr;
- if (eptr == mb->start_subject) prev_is_word = FALSE; else
- {
- if (eptr <= mb->start_used_ptr) mb->start_used_ptr = eptr - 1;
#ifdef SUPPORT_UNICODE
- if ((mb->poptions & PCRE2_UCP) != 0)
+ if (utf)
+ {
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
{
- c = eptr[-1];
- if (c == '_') prev_is_word = TRUE; else
+ int len = 1;
+ if (Feptr >= mb->end_subject)
{
- int cat = UCD_CATEGORY(c);
- prev_is_word = (cat == ucp_L || cat == ucp_N);
+ SCHECK_PARTIAL();
+ break;
}
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d || Loc == d) break;
+ Feptr += len;
}
- else
-#endif
- prev_is_word = MAX_255(eptr[-1])
- && ((mb->ctypes[eptr[-1]] & ctype_word) != 0);
- }
- /* Get status of next character */
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- cur_is_word = FALSE;
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM205);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
}
else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
{
- if (eptr >= mb->last_used_ptr) mb->last_used_ptr = eptr + 1;
-#ifdef SUPPORT_UNICODE
- if ((mb->poptions & PCRE2_UCP) != 0)
+ for (i = Lmin; i < Lmax; i++)
{
- c = *eptr;
- if (c == '_') cur_is_word = TRUE; else
+ if (Feptr >= mb->end_subject)
{
- int cat = UCD_CATEGORY(c);
- cur_is_word = (cat == ucp_L || cat == ucp_N);
+ SCHECK_PARTIAL();
+ break;
}
+ if (Lc == *Feptr || Loc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM30);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
}
- else
-#endif
- cur_is_word = MAX_255(*eptr)
- && ((mb->ctypes[*eptr] & ctype_word) != 0);
}
}
-
- /* Now see if the situation is what we want */
-
- if ((*ecode++ == OP_WORD_BOUNDARY)?
- cur_is_word == prev_is_word : cur_is_word != prev_is_word)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- /* Match any single character type except newline; have to take care with
- CRLF newlines and partial matching. */
-
- case OP_ANY:
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
- if (mb->partial != 0 &&
- eptr == mb->end_subject - 1 &&
- NLBLOCK->nltype == NLTYPE_FIXED &&
- NLBLOCK->nllen == 2 &&
- UCHAR21TEST(eptr) == NLBLOCK->nl[0])
- {
- mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
- }
-
- /* Fall through */
-
- /* Match any single character whatsoever. */
-
- case OP_ALLANY:
- if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */
- { /* not be updated before SCHECK_PARTIAL. */
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
}
- eptr++;
-#ifdef SUPPORT_UNICODE
- if (utf) ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
-#endif
- ecode++;
- break;
-
- /* Match a single code unit, even in UTF-8 mode. This opcode really does
- match any code unit, even newline. (It really should be called ANYCODEUNIT,
- of course - the byte name is from pre-16 bit days.) */
-
- case OP_ANYBYTE:
- if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */
- { /* not be updated before SCHECK_PARTIAL. */
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr++;
- ecode++;
- break;
-
- case OP_NOT_DIGIT:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c < 256 &&
-#endif
- (mb->ctypes[c] & ctype_digit) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
- case OP_DIGIT:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c > 255 ||
-#endif
- (mb->ctypes[c] & ctype_digit) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_NOT_WHITESPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c < 256 &&
-#endif
- (mb->ctypes[c] & ctype_space) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_WHITESPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c > 255 ||
-#endif
- (mb->ctypes[c] & ctype_space) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_NOT_WORDCHAR:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c < 256 &&
-#endif
- (mb->ctypes[c] & ctype_word) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_WORDCHAR:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_WIDE_CHARS
- c > 255 ||
-#endif
- (mb->ctypes[c] & ctype_word) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_ANYNL:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
-
- case CHAR_CR:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- }
- else if (UCHAR21TEST(eptr) == CHAR_LF) eptr++;
- break;
-
- case CHAR_LF:
- break;
-
- case CHAR_VT:
- case CHAR_FF:
- case CHAR_NEL:
-#ifndef EBCDIC
- case 0x2028:
- case 0x2029:
-#endif /* Not EBCDIC */
- if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
- break;
- }
- ecode++;
- break;
-
- case OP_NOT_HSPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */
- default: break;
- }
- ecode++;
- break;
-
- case OP_HSPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- HSPACE_CASES: break; /* Byte and multibyte cases */
- default: RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- break;
-
- case OP_NOT_VSPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- VSPACE_CASES: RRETURN(MATCH_NOMATCH);
- default: break;
- }
- ecode++;
- break;
+ /* Caseful comparisons */
- case OP_VSPACE:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
+ else
{
- VSPACE_CASES: break;
- default: RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- break;
-
#ifdef SUPPORT_UNICODE
- /* Check the next character by Unicode property. We will get here only
- if the support is in the binary; otherwise a compile-time error occurs. */
-
- case OP_PROP:
- case OP_NOTPROP:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- {
- const uint32_t *cp;
- const ucd_record *prop = GET_UCD(c);
-
- switch(ecode[1])
+ if (utf)
{
- case PT_ANY:
- if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_LAMP:
- if ((prop->chartype == ucp_Lu ||
- prop->chartype == ucp_Ll ||
- prop->chartype == ucp_Lt) == (op == OP_NOTPROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_GC:
- if ((ecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_PC:
- if ((ecode[2] != prop->chartype) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_SC:
- if ((ecode[2] != prop->script) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- /* These are specials */
-
- case PT_ALNUM:
- if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
- PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (op == OP_NOTPROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- /* Perl space used to exclude VT, but from Perl 5.18 it is included,
- which means that Perl space and POSIX space are now identical. PCRE
- was changed at release 8.34. */
-
- case PT_SPACE: /* Perl space */
- case PT_PXSPACE: /* POSIX space */
- switch(c)
- {
- HSPACE_CASES:
- VSPACE_CASES:
- if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
- break;
-
- default:
- if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) ==
- (op == OP_NOTPROP)) RRETURN(MATCH_NOMATCH);
- break;
- }
- break;
-
- case PT_WORD:
- if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
- PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
- c == CHAR_UNDERSCORE) == (op == OP_NOTPROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_CLIST:
- cp = PRIV(ucd_caseless_sets) + ecode[2];
- for (;;)
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
{
- if (c < *cp)
- { if (op == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; }
- if (c == *cp++)
- { if (op == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } }
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
}
- break;
-
- case PT_UCNC:
- if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
- c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
- c >= 0xe000) == (op == OP_NOTPROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- /* This should never occur */
-
- default:
- RRETURN(PCRE2_ERROR_INTERNAL);
- }
-
- ecode += 3;
- }
- break;
-
- /* Match an extended Unicode sequence. We will get here only if the support
- is in the binary; otherwise a compile-time error occurs. */
-
- case OP_EXTUNI:
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- else
- {
- int lgb, rgb;
- GETCHARINCTEST(c, eptr);
- lgb = UCD_GRAPHBREAK(c);
- while (eptr < mb->end_subject)
- {
- int len = 1;
- if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
- rgb = UCD_GRAPHBREAK(c);
- if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
- lgb = rgb;
- eptr += len;
- }
- }
- CHECK_PARTIAL();
- ecode++;
- break;
-#endif /* SUPPORT_UNICODE */
-
-
- /* Match a back reference, possibly repeatedly. Look past the end of the
- item to see if there is repeat information following.
-
- The OP_REF and OP_REFI opcodes are used for a reference to a numbered group
- or to a non-duplicated named group. For a duplicated named group, OP_DNREF
- and OP_DNREFI are used. In this case we must scan the list of groups to
- which the name refers, and use the first one that is set. */
-
- case OP_DNREF:
- case OP_DNREFI:
- caseless = op == OP_DNREFI;
- {
- int count = GET2(ecode, 1+IMM2_SIZE);
- PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size;
- ecode += 1 + 2*IMM2_SIZE;
-
- /* Initializing 'offset' avoids a compiler warning in the REF_REPEAT
- code. */
-
- offset = 0;
- while (count-- > 0)
- {
- offset = GET2(slot, 0) << 1;
- if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) break;
- slot += mb->name_entry_size;
}
- }
- goto REF_REPEAT;
-
- case OP_REF:
- case OP_REFI:
- caseless = op == OP_REFI;
- offset = GET2(ecode, 1) << 1; /* Doubled ref number */
- ecode += 1 + IMM2_SIZE;
-
- /* Set up for repetition, or handle the non-repeated case */
-
- REF_REPEAT:
- switch (*ecode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- c = *ecode++ - OP_CRSTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 1 + IMM2_SIZE);
- if (max == 0) max = INT_MAX;
- ecode += 1 + 2 * IMM2_SIZE;
- break;
-
- default: /* No repeat follows */
+ else
+#endif
+ /* Not UTF mode */
{
- int rc = match_ref(offset, offset_top, eptr, mb, caseless, &length);
- if (rc != 0)
+ for (i = 1; i <= Lmin; i++)
{
- if (rc > 0) eptr = mb->end_subject; /* Partial match */
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
}
}
- eptr += length;
- continue; /* With the main loop */
- }
-
- /* Handle repeated back references. If a set group has length zero, just
- continue with the main loop, because it matches however many times. For an
- unset reference, if the minimum is zero, we can also just continue. We an
- also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset
- group be have as a zero-length group. For any other unset cases, carrying
- on will result in NOMATCH. */
-
- if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET)
- {
- if (mb->ovector[offset] == mb->ovector[offset + 1]) continue;
- }
- else /* Group is not set */
- {
- if (min == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
- continue;
- }
-
- /* First, ensure the minimum number of matches are present. */
-
- for (i = 1; i <= min; i++)
- {
- PCRE2_SIZE slength;
- int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength);
- if (rc != 0)
- {
- if (rc > 0) eptr = mb->end_subject; /* Partial match */
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += slength;
- }
- /* If min = max, continue at the same level without recursion.
- They are not both allowed to be zero. */
+ if (Lmin == Lmax) continue;
- if (min == max) continue;
-
- /* If minimizing, keep trying and advancing the pointer */
-
- if (minimize)
- {
- for (fi = min;; fi++)
+ if (reptype == REPTYPE_MIN)
{
- int rc;
- PCRE2_SIZE slength;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM14);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength);
- if (rc != 0)
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- if (rc > 0) eptr = mb->end_subject; /* Partial match */
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
+ uint32_t d;
+ for (;;)
+ {
+ RMATCH(Fecode, RM206);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
+ }
}
- eptr += slength;
- }
- /* Control never gets here */
- }
-
- /* If maximizing, find the longest string and work backwards, as long as
- the matched lengths for each iteration are the same. */
-
- else
- {
- BOOL samelengths = TRUE;
- pp = eptr;
- length = mb->ovector[offset+1] - mb->ovector[offset];
-
- for (i = min; i < max; i++)
- {
- PCRE2_SIZE slength;
- int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength);
-
- if (rc != 0)
+ else
+#endif
+ /* Not UTF mode */
{
- /* Can't use CHECK_PARTIAL because we don't want to update eptr in
- the soft partial matching case. */
-
- if (rc > 0 && mb->partial != 0 &&
- mb->end_subject > mb->start_used_ptr)
+ for (;;)
{
- mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ RMATCH(Fecode, RM31);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
}
- break;
}
-
- if (slength != length) samelengths = FALSE;
- eptr += slength;
+ /* Control never gets here */
}
- /* If the length matched for each repetition is the same as the length of
- the captured group, we can easily work backwards. This is the normal
- case. However, in caseless UTF-8 mode there are pairs of case-equivalent
- characters whose lengths (in terms of code units) differ. However, this
- is very rare, so we handle it by re-matching fewer and fewer times. */
+ /* Maximize case */
- if (samelengths)
+ else
{
- while (eptr >= pp)
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM15);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr -= length;
- }
- }
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d) break;
+ Feptr += len;
+ }
- /* The rare case of non-matching lengths. Re-scan the repetition for each
- iteration. We know that match_ref() will succeed every time. */
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
- else
- {
- max = i;
- for (;;)
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM207);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM68);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr == pp) break; /* Failed after minimal repetition */
- eptr = pp;
- max--;
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- PCRE2_SIZE slength;
- (void)match_ref(offset, offset_top, eptr, mb, caseless, &slength);
- eptr += slength;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (Lc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM32);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
}
}
}
-
- RRETURN(MATCH_NOMATCH);
}
- /* Control never gets here */
+ break;
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
- /* Match a bit-mapped character class, possibly repeatedly. This op code is
- used when all the characters in the class have values in the range 0-255,
- and either the matching is caseful, or the characters are in the range
- 0-127 when UTF-8 processing is enabled. The only difference between
+
+ /* ===================================================================== */
+ /* Match a bit-mapped character class, possibly repeatedly. These op codes
+ are used when all the characters in the class have values in the range
+ 0-255, and either the matching is caseful, or the characters are in the
+ range 0-127 when UTF processing is enabled. The only difference between
OP_CLASS and OP_NCLASS occurs when a data character outside the range is
- encountered.
+ encountered. */
- First, look past the end of the item to see if there is repeat information
- following. Then obey similar code to character type repeats - written out
- again for speed. */
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lbyte_map_address F->temp_sptr[1]
+#define Lbyte_map ((unsigned char *)Lbyte_map_address)
case OP_NCLASS:
case OP_CLASS:
{
- /* The data variable is saved across frames, so the byte map needs to
- be stored there. */
-#define BYTE_MAP ((uint8_t *)data)
- data = ecode + 1; /* Save for matching */
- ecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */
+ Lbyte_map_address = Fecode + 1; /* Save for matching */
+ Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */
+
+ /* Look past the end of the item to see if there is repeat information
+ following. Then obey similar code to character type repeats. */
- switch (*ecode)
+ switch (*Fecode)
{
case OP_CRSTAR:
case OP_CRMINSTAR:
@@ -2994,27 +1809,24 @@ for (;;)
case OP_CRPOSSTAR:
case OP_CRPOSPLUS:
case OP_CRPOSQUERY:
- c = *ecode++ - OP_CRSTAR;
- if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0;
- else possessive = TRUE;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
break;
case OP_CRRANGE:
case OP_CRMINRANGE:
case OP_CRPOSRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- possessive = (*ecode == OP_CRPOSRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 1 + IMM2_SIZE);
- if (max == 0) max = INT_MAX;
- ecode += 1 + 2 * IMM2_SIZE;
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
break;
default: /* No repeat follows */
- min = max = 1;
+ Lmin = Lmax = 1;
break;
}
@@ -3023,100 +1835,99 @@ for (;;)
#ifdef SUPPORT_UNICODE
if (utf)
{
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- if (c > 255)
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
{
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
}
else
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
}
}
else
#endif
/* Not UTF mode */
{
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- c = *eptr++;
+ fc = *Feptr++;
#if PCRE2_CODE_UNIT_WIDTH != 8
- if (c > 255)
+ if (fc > 255)
{
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
}
else
#endif
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
}
}
- /* If max == min we can continue with the main loop without the
- need to recurse. */
+ /* If Lmax == Lmin we are done. Continue with main loop. */
- if (min == max) continue;
+ if (Lmin == Lmax) continue;
/* If minimizing, keep testing the rest of the expression and advancing
the pointer while it matches the class. */
- if (minimize)
+ if (reptype == REPTYPE_MIN)
{
#ifdef SUPPORT_UNICODE
if (utf)
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM16);
+ RMATCH(Fecode, RM200);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- if (c > 255)
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
{
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
}
else
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
}
}
else
#endif
/* Not UTF mode */
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM17);
+ RMATCH(Fecode, RM23);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- c = *eptr++;
+ fc = *Feptr++;
#if PCRE2_CODE_UNIT_WIDTH != 8
- if (c > 255)
+ if (fc > 255)
{
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
}
else
#endif
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
}
}
/* Control never gets here */
@@ -3126,91 +1937,102 @@ for (;;)
else
{
- pp = eptr;
+ Lstart_eptr = Feptr;
#ifdef SUPPORT_UNICODE
if (utf)
{
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c > 255)
+ GETCHARLEN(fc, Feptr, len);
+ if (fc > 255)
{
- if (op == OP_CLASS) break;
+ if (Fop == OP_CLASS) break;
}
else
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break;
- eptr += len;
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break;
+ Feptr += len;
}
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM18);
+ RMATCH(Fecode, RM201);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- BACKCHAR(eptr);
+ if (Feptr-- == Lstart_eptr) break; /* Tried at original position */
+ BACKCHAR(Feptr);
}
}
else
#endif
/* Not UTF mode */
{
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- c = *eptr;
+ fc = *Feptr;
#if PCRE2_CODE_UNIT_WIDTH != 8
- if (c > 255)
+ if (fc > 255)
{
- if (op == OP_CLASS) break;
+ if (Fop == OP_CLASS) break;
}
else
#endif
- if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break;
- eptr++;
+ if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break;
+ Feptr++;
}
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
- while (eptr >= pp)
+ while (Feptr >= Lstart_eptr)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM19);
+ RMATCH(Fecode, RM24);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
+ Feptr--;
}
}
RRETURN(MATCH_NOMATCH);
}
-#undef BYTE_MAP
}
/* Control never gets here */
+#undef Lbyte_map_address
+#undef Lbyte_map
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+
+ /* ===================================================================== */
/* Match an extended character class. In the 8-bit library, this opcode is
encountered only when UTF-8 mode mode is supported. In the 16-bit and
32-bit libraries, codepoints greater than 255 may be encountered even when
UTF is not supported. */
+#define Lstart_eptr F->temp_sptr[0]
+#define Lxclass_data F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+
#ifdef SUPPORT_WIDE_CHARS
case OP_XCLASS:
{
- data = ecode + 1 + LINK_SIZE; /* Save for matching */
- ecode += GET(ecode, 1); /* Advance past the item */
+ Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */
+ Fecode += GET(Fecode, 1); /* Advance past the item */
- switch (*ecode)
+ switch (*Fecode)
{
case OP_CRSTAR:
case OP_CRMINSTAR:
@@ -3221,65 +2043,61 @@ for (;;)
case OP_CRPOSSTAR:
case OP_CRPOSPLUS:
case OP_CRPOSQUERY:
- c = *ecode++ - OP_CRSTAR;
- if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0;
- else possessive = TRUE;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
break;
case OP_CRRANGE:
case OP_CRMINRANGE:
case OP_CRPOSRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- possessive = (*ecode == OP_CRPOSRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 1 + IMM2_SIZE);
- if (max == 0) max = INT_MAX;
- ecode += 1 + 2 * IMM2_SIZE;
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
break;
default: /* No repeat follows */
- min = max = 1;
+ Lmin = Lmax = 1;
break;
}
/* First, ensure the minimum number of matches are present. */
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
}
- /* If max == min we can continue with the main loop without the
- need to recurse. */
+ /* If Lmax == Lmin we can just continue with the main loop. */
- if (min == max) continue;
+ if (Lmin == Lmax) continue;
/* If minimizing, keep testing the rest of the expression and advancing
the pointer while it matches the class. */
- if (minimize)
+ if (reptype == REPTYPE_MIN)
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM20);
+ RMATCH(Fecode, RM100);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
}
@@ -3288,33 +2106,33 @@ for (;;)
else
{
- pp = eptr;
- for (i = min; i < max; i++)
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
#ifdef SUPPORT_UNICODE
- GETCHARLENTEST(c, eptr, len);
+ GETCHARLENTEST(fc, Feptr, len);
#else
- c = *eptr;
+ fc = *Feptr;
#endif
- if (!PRIV(xclass)(c, data, utf)) break;
- eptr += len;
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) break;
+ Feptr += len;
}
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
for(;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM21);
+ RMATCH(Fecode, RM101);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
+ if (Feptr-- == Lstart_eptr) break; /* Tried at original position */
#ifdef SUPPORT_UNICODE
- if (utf) BACKCHAR(eptr);
+ if (utf) BACKCHAR(Feptr);
#endif
}
RRETURN(MATCH_NOMATCH);
@@ -3322,887 +2140,370 @@ for (;;)
/* Control never gets here */
}
-#endif /* End of XCLASS */
+#endif /* SUPPORT_WIDE_CHARS: end of XCLASS */
- /* Match a single character, casefully */
+#undef Lstart_eptr
+#undef Lxclass_data
+#undef Lmin
+#undef Lmax
- case OP_CHAR:
-#ifdef SUPPORT_UNICODE
- if (utf)
+
+ /* ===================================================================== */
+ /* Match various character types when PCRE2_UCP is not set. These opcodes
+ are not generated when PCRE2_UCP is set - instead appropriate property
+ tests are compiled. */
+
+ case OP_NOT_DIGIT:
+ if (Feptr >= mb->end_subject)
{
- length = 1;
- ecode++;
- GETCHARLEN(fc, ecode, length);
- if (length > (PCRE2_SIZE)(mb->end_subject - eptr))
- {
- CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
- for (; length > 0; length--)
- {
- if (*ecode++ != UCHAR21INC(eptr)) RRETURN(MATCH_NOMATCH);
- }
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- else
-#endif
- /* Not UTF mode */
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_DIGIT:
+ if (Feptr >= mb->end_subject)
{
- if (mb->end_subject - eptr < 1)
- {
- SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
- if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH);
- ecode += 2;
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
break;
- /* Match a single character, caselessly. If we are at the end of the
- subject, give up immediately. */
-
- case OP_CHARI:
- if (eptr >= mb->end_subject)
+ case OP_NOT_WHITESPACE:
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
-#ifdef SUPPORT_UNICODE
- if (utf)
+ case OP_WHITESPACE:
+ if (Feptr >= mb->end_subject)
{
- length = 1;
- ecode++;
- GETCHARLEN(fc, ecode, length);
-
- /* If the pattern character's value is < 128, we have only one byte, and
- we know that its other case must also be one byte long, so we can use the
- fast lookup table. We know that there is at least one byte left in the
- subject. */
-
- if (fc < 128)
- {
- uint32_t cc = UCHAR21(eptr);
- if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
- ecode++;
- eptr++;
- }
-
- /* Otherwise we must pick up the subject character. Note that we cannot
- use the value of "length" to check for sufficient bytes left, because the
- other case of the character may have more or fewer bytes. */
-
- else
- {
- uint32_t dc;
- GETCHARINC(dc, eptr);
- ecode += length;
-
- /* If we have Unicode property support, we can use it to test the other
- case of the character, if there is one. */
-
- if (fc != dc)
- {
-#ifdef SUPPORT_UNICODE
- if (dc != UCD_OTHERCASE(fc))
-#endif
- RRETURN(MATCH_NOMATCH);
- }
- }
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- else
-#endif /* SUPPORT_UNICODE */
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
- /* Not UTF mode */
+ case OP_NOT_WORDCHAR:
+ if (Feptr >= mb->end_subject)
{
- if (TABLE_GET(ecode[1], mb->lcc, ecode[1])
- != TABLE_GET(*eptr, mb->lcc, *eptr)) RRETURN(MATCH_NOMATCH);
- eptr++;
- ecode += 2;
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
break;
- /* Match a single character repeatedly. */
-
- case OP_EXACT:
- case OP_EXACTI:
- min = max = GET2(ecode, 1);
- ecode += 1 + IMM2_SIZE;
- goto REPEATCHAR;
-
- case OP_POSUPTO:
- case OP_POSUPTOI:
- possessive = TRUE;
- /* Fall through */
-
- case OP_UPTO:
- case OP_UPTOI:
- case OP_MINUPTO:
- case OP_MINUPTOI:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_MINUPTO || *ecode == OP_MINUPTOI;
- ecode += 1 + IMM2_SIZE;
- goto REPEATCHAR;
-
- case OP_POSSTAR:
- case OP_POSSTARI:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
- goto REPEATCHAR;
-
- case OP_POSPLUS:
- case OP_POSPLUSI:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
- goto REPEATCHAR;
-
- case OP_POSQUERY:
- case OP_POSQUERYI:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
- goto REPEATCHAR;
-
- case OP_STAR:
- case OP_STARI:
- case OP_MINSTAR:
- case OP_MINSTARI:
- case OP_PLUS:
- case OP_PLUSI:
- case OP_MINPLUS:
- case OP_MINPLUSI:
- case OP_QUERY:
- case OP_QUERYI:
- case OP_MINQUERY:
- case OP_MINQUERYI:
- c = *ecode++ - ((op < OP_STARI)? OP_STAR : OP_STARI);
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
-
- /* Common code for all repeated single-character matches. We first check
- for the minimum number of characters. If the minimum equals the maximum, we
- are done. Otherwise, if minimizing, check the rest of the pattern for a
- match; if there isn't one, advance up to the maximum, one character at a
- time.
-
- If maximizing, advance up to the maximum number of matching characters,
- until eptr is past the end of the maximum run. If possessive, we are
- then done (no backing up). Otherwise, match at this position; anything
- other than no match is immediately returned. For nomatch, back up one
- character, unless we are matching \R and the last thing matched was
- \r\n, in which case, back up two bytes. When we reach the first optional
- character position, we can save stack by doing a tail recurse.
-
- The various UTF/non-UTF and caseful/caseless cases are handled separately,
- for speed. */
-
- REPEATCHAR:
-#ifdef SUPPORT_UNICODE
- if (utf)
+ case OP_WORDCHAR:
+ if (Feptr >= mb->end_subject)
{
- length = 1;
- charptr = ecode;
- GETCHARLEN(fc, ecode, length);
- ecode += length;
-
- /* Handle multibyte character matching specially here. There is
- support for caseless matching if UCP support is present. */
-
- if (length > 1)
- {
- uint32_t othercase;
- if (op >= OP_STARI && /* Caseless */
- (othercase = UCD_OTHERCASE(fc)) != fc)
- oclength = PRIV(ord2utf)(othercase, occhars);
- else oclength = 0;
-
- for (i = 1; i <= min; i++)
- {
- if (eptr <= mb->end_subject - length &&
- memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length;
- else if (oclength > 0 &&
- eptr <= mb->end_subject - oclength &&
- memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength;
- else
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM22);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr <= mb->end_subject - length &&
- memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length;
- else if (oclength > 0 &&
- eptr <= mb->end_subject - oclength &&
- memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength;
- else
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (eptr <= mb->end_subject - length &&
- memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length;
- else if (oclength > 0 &&
- eptr <= mb->end_subject - oclength &&
- memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength;
- else
- {
- CHECK_PARTIAL();
- break;
- }
- }
-
- if (possessive) continue; /* No backtracking */
-
- /* After \C in UTF mode, pp might be in the middle of a Unicode
- character. Use <= pp to ensure backtracking doesn't go too far. */
-
- for(;;)
- {
- if (eptr <= pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM23);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- BACKCHAR(eptr);
- }
- }
- /* Control never gets here */
- }
-
- /* If the length of a UTF-8 character is 1, we fall through here, and
- obey the code as for non-UTF-8 characters below, though in this case the
- value of fc will always be < 128. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- else
-#endif /* SUPPORT_UNICODE */
-
- /* When not in UTF-8 mode, load a single-byte character. */
- fc = *ecode++;
-
- /* The value of fc at this point is always one character, though we may
- or may not be in UTF mode. The code is duplicated for the caseless and
- caseful cases, for speed, since matching characters is likely to be quite
- common. First, ensure the minimum number of matches are present. If min =
- max, continue at the same level without recursing. Otherwise, if
- minimizing, keep trying the rest of the expression and advancing one
- matching character if failing, up to the maximum. Alternatively, if
- maximizing, find the maximum number of characters and work backwards. */
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
- if (op >= OP_STARI) /* Caseless */
+ case OP_ANYNL:
+ if (Feptr >= mb->end_subject)
{
-#if PCRE2_CODE_UNIT_WIDTH == 8
- /* fc must be < 128 if UTF is enabled. */
- foc = mb->fcc[fc];
-#else
-#ifdef SUPPORT_UNICODE
- if (utf && fc > 127)
- foc = UCD_OTHERCASE(fc);
- else
-#endif /* SUPPORT_UNICODE */
- foc = TABLE_GET(fc, mb->fcc, fc);
-#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
-
- for (i = 1; i <= min; i++)
- {
- uint32_t cc; /* Faster than PCRE2_UCHAR */
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- cc = UCHAR21TEST(eptr);
- if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH);
- eptr++;
- }
- if (min == max) continue;
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- uint32_t cc; /* Faster than PCRE2_UCHAR */
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM24);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- cc = UCHAR21TEST(eptr);
- if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH);
- eptr++;
- }
- /* Control never gets here */
- }
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- uint32_t cc; /* Faster than PCRE2_UCHAR */
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- cc = UCHAR21TEST(eptr);
- if (fc != cc && foc != cc) break;
- eptr++;
- }
- if (possessive) continue; /* No backtracking */
- for (;;)
- {
- if (eptr == pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM25);
- eptr--;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- }
- /* Control never gets here */
- }
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
-
- /* Caseful comparisons (includes all multi-byte characters) */
-
- else
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
{
- for (i = 1; i <= min; i++)
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr >= mb->end_subject)
{
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH);
+ SCHECK_PARTIAL();
}
+ else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++;
+ break;
- if (min == max) continue;
+ case CHAR_LF:
+ break;
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM26);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc != UCHAR21TEST(eptr)) break;
- eptr++;
- }
- if (possessive) continue; /* No backtracking */
- for (;;)
- {
- if (eptr == pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM27);
- eptr--;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- }
- /* Control never gets here */
- }
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
}
- /* Control never gets here */
+ Fecode++;
+ break;
- /* Match a negated single one-byte character. The character we are
- checking can be multibyte. */
+ case OP_NOT_HSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */
+ default: break;
+ }
+ Fecode++;
+ break;
- case OP_NOT:
- case OP_NOTI:
- if (eptr >= mb->end_subject)
+ case OP_HSPACE:
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
-#ifdef SUPPORT_UNICODE
- if (utf)
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
{
- uint32_t ch, och;
+ HSPACE_CASES: break; /* Byte and multibyte cases */
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ Fecode++;
+ break;
- ecode++;
- GETCHARINC(ch, ecode);
- GETCHARINC(c, eptr);
+ case OP_NOT_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ Fecode++;
+ break;
- if (op == OP_NOT)
- {
- if (ch == c) RRETURN(MATCH_NOMATCH);
- }
- else
- {
- if (ch > 127)
- och = UCD_OTHERCASE(ch);
- else
- och = TABLE_GET(ch, mb->fcc, ch);
- if (ch == c || och == c) RRETURN(MATCH_NOMATCH);
- }
+ case OP_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
}
- else
-#endif /* SUPPORT_UNICODE */
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
{
- uint32_t ch = ecode[1];
- c = *eptr++;
- if (ch == c || (op == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == c))
- RRETURN(MATCH_NOMATCH);
- ecode += 2;
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
}
+ Fecode++;
break;
- /* Match a negated single one-byte character repeatedly. This is almost a
- repeat of the code for a repeated single character, but I haven't found a
- nice way of commoning these up that doesn't require a test of the
- positive/negative option for each character match. Maybe that wouldn't add
- very much to the time taken, but character matching *is* what this is all
- about... */
- case OP_NOTEXACT:
- case OP_NOTEXACTI:
- min = max = GET2(ecode, 1);
- ecode += 1 + IMM2_SIZE;
- goto REPEATNOTCHAR;
+#ifdef SUPPORT_UNICODE
- case OP_NOTUPTO:
- case OP_NOTUPTOI:
- case OP_NOTMINUPTO:
- case OP_NOTMINUPTOI:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_NOTMINUPTO || *ecode == OP_NOTMINUPTOI;
- ecode += 1 + IMM2_SIZE;
- goto REPEATNOTCHAR;
+ /* ===================================================================== */
+ /* Check the next character by Unicode property. We will get here only
+ if the support is in the binary; otherwise a compile-time error occurs. */
- case OP_NOTPOSSTAR:
- case OP_NOTPOSSTARI:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
- goto REPEATNOTCHAR;
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ {
+ const uint32_t *cp;
+ const ucd_record *prop = GET_UCD(fc);
- case OP_NOTPOSPLUS:
- case OP_NOTPOSPLUSI:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
- goto REPEATNOTCHAR;
+ switch(Fecode[1])
+ {
+ case PT_ANY:
+ if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
- case OP_NOTPOSQUERY:
- case OP_NOTPOSQUERYI:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
- goto REPEATNOTCHAR;
+ case PT_LAMP:
+ if ((prop->chartype == ucp_Lu ||
+ prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == (Fop == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- case OP_NOTPOSUPTO:
- case OP_NOTPOSUPTOI:
- possessive = TRUE;
- min = 0;
- max = GET2(ecode, 1);
- ecode += 1 + IMM2_SIZE;
- goto REPEATNOTCHAR;
+ case PT_GC:
+ if ((Fecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (Fop == OP_PROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- case OP_NOTSTAR:
- case OP_NOTSTARI:
- case OP_NOTMINSTAR:
- case OP_NOTMINSTARI:
- case OP_NOTPLUS:
- case OP_NOTPLUSI:
- case OP_NOTMINPLUS:
- case OP_NOTMINPLUSI:
- case OP_NOTQUERY:
- case OP_NOTQUERYI:
- case OP_NOTMINQUERY:
- case OP_NOTMINQUERYI:
- c = *ecode++ - ((op >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR);
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
+ case PT_PC:
+ if ((Fecode[2] != prop->chartype) == (Fop == OP_PROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- /* Common code for all repeated single-byte matches. */
+ case PT_SC:
+ if ((Fecode[2] != prop->script) == (Fop == OP_PROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- REPEATNOTCHAR:
- GETCHARINCTEST(fc, ecode);
+ /* These are specials */
- /* The code is duplicated for the caseless and caseful cases, for speed,
- since matching characters is likely to be quite common. First, ensure the
- minimum number of matches are present. If min = max, continue at the same
- level without recursing. Otherwise, if minimizing, keep trying the rest of
- the expression and advancing one matching character if failing, up to the
- maximum. Alternatively, if maximizing, find the maximum number of
- characters and work backwards. */
+ case PT_ALNUM:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (Fop == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- if (op >= OP_NOTSTARI) /* Caseless */
- {
-#ifdef SUPPORT_UNICODE
- if (utf && fc > 127)
- foc = UCD_OTHERCASE(fc);
- else
-#endif /* SUPPORT_UNICODE */
- foc = TABLE_GET(fc, mb->fcc, fc);
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (i = 1; i <= min; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif /* SUPPORT_UNICODE */
- /* Not UTF mode */
- {
- for (i = 1; i <= min; i++)
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(fc)
{
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH);
- eptr++;
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) ==
+ (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH);
+ break;
}
- }
+ break;
- if (min == max) continue;
+ case PT_WORD:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ fc == CHAR_UNDERSCORE) == (Fop == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- if (minimize)
- {
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM28);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif /*SUPPORT_UNICODE */
- /* Not UTF mode */
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + Fecode[2];
+ for (;;)
{
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM29);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH);
- eptr++;
- }
+ if (fc < *cp)
+ { if (Fop == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; }
+ if (fc == *cp++)
+ { if (Fop == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } }
}
- /* Control never gets here */
- }
-
- /* Maximize case */
-
- else
- {
- pp = eptr;
+ break;
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(d, eptr, len);
- if (fc == d || (uint32_t)foc == d) break;
- eptr += len;
- }
- if (possessive) continue; /* No backtracking */
+ case PT_UCNC:
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Fop == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
- /* After \C in UTF mode, pp might be in the middle of a Unicode
- character. Use <= pp to ensure backtracking doesn't go too far. */
+ /* This should never occur */
- for(;;)
- {
- if (eptr <= pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM30);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- BACKCHAR(eptr);
- }
- }
- else
-#endif /* SUPPORT_UNICODE */
- /* Not UTF mode */
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc == *eptr || foc == *eptr) break;
- eptr++;
- }
- if (possessive) continue; /* No backtracking */
- for (;;)
- {
- if (eptr == pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM31);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- }
- }
- /* Control never gets here */
+ default:
+ return PCRE2_ERROR_INTERNAL;
}
+
+ Fecode += 3;
}
+ break;
- /* Caseful comparisons */
+ /* ===================================================================== */
+ /* Match an extended Unicode sequence. We will get here only if the support
+ is in the binary; otherwise a compile-time error occurs. */
+
+ case OP_EXTUNI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
else
{
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (i = 1; i <= min; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
- /* Not UTF mode */
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
- }
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM32);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
- /* Not UTF mode */
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM33);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- /* Maximize case */
-
- else
- {
- pp = eptr;
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf,
+ NULL);
+ }
+ CHECK_PARTIAL();
+ Fecode++;
+ break;
-#ifdef SUPPORT_UNICODE
- if (utf)
- {
- uint32_t d;
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(d, eptr, len);
- if (fc == d) break;
- eptr += len;
- }
- if (possessive) continue; /* No backtracking */
+#endif /* SUPPORT_UNICODE */
- /* After \C in UTF mode, pp might be in the middle of a Unicode
- character. Use <= pp to ensure backtracking doesn't go too far. */
- for(;;)
- {
- if (eptr <= pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM34);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- BACKCHAR(eptr);
- }
- }
- else
-#endif
- /* Not UTF mode */
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= mb->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc == *eptr) break;
- eptr++;
- }
- if (possessive) continue; /* No backtracking */
- for (;;)
- {
- if (eptr == pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM35);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- }
- }
- /* Control never gets here */
- }
- }
- /* Control never gets here */
+ /* ===================================================================== */
+ /* Match a single character type repeatedly. Note that the property type
+ does not need to be in a stack frame as it not used within an RMATCH()
+ loop. */
- /* Match a single character type repeatedly; several different opcodes
- share code. This is very similar to the code for single characters, but we
- repeat it in the interests of efficiency. */
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lctype F->temp_32[2]
+#define Lpropvalue F->temp_32[3]
case OP_TYPEEXACT:
- min = max = GET2(ecode, 1);
- minimize = TRUE;
- ecode += 1 + IMM2_SIZE;
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
goto REPEATTYPE;
case OP_TYPEUPTO:
case OP_TYPEMINUPTO:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_TYPEMINUPTO;
- ecode += 1 + IMM2_SIZE;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
goto REPEATTYPE;
case OP_TYPEPOSSTAR:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
goto REPEATTYPE;
case OP_TYPEPOSPLUS:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
goto REPEATTYPE;
case OP_TYPEPOSQUERY:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
goto REPEATTYPE;
case OP_TYPEPOSUPTO:
- possessive = TRUE;
- min = 0;
- max = GET2(ecode, 1);
- ecode += 1 + IMM2_SIZE;
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
goto REPEATTYPE;
case OP_TYPESTAR:
@@ -4211,127 +2512,122 @@ for (;;)
case OP_TYPEMINPLUS:
case OP_TYPEQUERY:
case OP_TYPEMINQUERY:
- c = *ecode++ - OP_TYPESTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
+ fc = *Fecode++ - OP_TYPESTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
- /* Common code for all repeated single character type matches. Note that
- in UTF-8 mode, '.' matches a character of any length, but for the other
- character types, the valid characters are all one-byte long. */
+ /* Common code for all repeated character type matches. */
REPEATTYPE:
- ctype = *ecode++; /* Code for the character type */
+ Lctype = *Fecode++; /* Code for the character type */
#ifdef SUPPORT_UNICODE
- if (ctype == OP_PROP || ctype == OP_NOTPROP)
+ if (Lctype == OP_PROP || Lctype == OP_NOTPROP)
{
- prop_fail_result = ctype == OP_NOTPROP;
- prop_type = *ecode++;
- prop_value = *ecode++;
+ proptype = *Fecode++;
+ Lpropvalue = *Fecode++;
}
- else prop_type = -1;
+ else proptype = -1;
#endif
/* First, ensure the minimum number of matches are present. Use inline
code for maximizing the speed, and do the type test once at the start
- (i.e. keep it out of the loop). Separate the UTF-8 code completely as that
- is tidier. Also separate the UCP code, which can be the same for both UTF-8
- and single-bytes. */
+ (i.e. keep it out of the loop). The code for UTF mode is separated out for
+ tidiness, except for Unicode property tests. */
- if (min > 0)
+ if (Lmin > 0)
{
#ifdef SUPPORT_UNICODE
- if (prop_type >= 0)
+ if (proptype >= 0) /* Property tests in all modes */
{
- switch(prop_type)
+ switch(proptype)
{
case PT_ANY:
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
- for (i = 1; i <= min; i++)
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
+ GETCHARINCTEST(fc, Feptr);
}
break;
case PT_LAMP:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
int chartype;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- chartype = UCD_CHARTYPE(c);
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
if ((chartype == ucp_Lu ||
chartype == ucp_Ll ||
- chartype == ucp_Lt) == prop_fail_result)
+ chartype == ucp_Lt) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
case PT_GC:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
case PT_PC:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
case PT_SC:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
case PT_ALNUM:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
int category;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- category = UCD_CATEGORY(c);
- if ((category == ucp_L || category == ucp_N) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
@@ -4342,23 +2638,23 @@ for (;;)
case PT_SPACE: /* Perl space */
case PT_PXSPACE: /* POSIX space */
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- switch(c)
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
{
HSPACE_CASES:
VSPACE_CASES:
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
break;
default:
- if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result)
+ if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
break;
}
@@ -4366,55 +2662,61 @@ for (;;)
break;
case PT_WORD:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
int category;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- category = UCD_CATEGORY(c);
- if ((category == ucp_L || category == ucp_N || c == CHAR_UNDERSCORE)
- == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
case PT_CLIST:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
const uint32_t *cp;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- cp = PRIV(ucd_caseless_sets) + prop_value;
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
for (;;)
{
- if (c < *cp)
- { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } }
- if (c == *cp++)
- { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; }
+ if (fc < *cp)
+ {
+ if (Lctype == OP_NOTPROP) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+ }
}
}
break;
case PT_UCNC:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
- c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
- c >= 0xe000) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
break;
@@ -4422,105 +2724,95 @@ for (;;)
/* This should not occur */
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
}
/* Match extended Unicode sequences. We will get here only if the
support is in the binary; otherwise a compile-time error occurs. */
- else if (ctype == OP_EXTUNI)
+ else if (Lctype == OP_EXTUNI)
{
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
else
{
- int lgb, rgb;
- GETCHARINCTEST(c, eptr);
- lgb = UCD_GRAPHBREAK(c);
- while (eptr < mb->end_subject)
- {
- int len = 1;
- if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
- rgb = UCD_GRAPHBREAK(c);
- if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
- lgb = rgb;
- eptr += len;
- }
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject,
+ mb->end_subject, utf, NULL);
}
CHECK_PARTIAL();
}
}
-
else
#endif /* SUPPORT_UNICODE */
-/* Handle all other cases when the coding is UTF-8 */
+/* Handle all other cases in UTF mode */
#ifdef SUPPORT_UNICODE
- if (utf) switch(ctype)
+ if (utf) switch(Lctype)
{
case OP_ANY:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
if (mb->partial != 0 &&
- eptr + 1 >= mb->end_subject &&
+ Feptr + 1 >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- UCHAR21(eptr) == NLBLOCK->nl[0])
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
break;
case OP_ALLANY:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
break;
case OP_ANYBYTE:
- if (eptr > mb->end_subject - min) RRETURN(MATCH_NOMATCH);
- eptr += min;
+ if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH);
+ Feptr += Lmin;
break;
case OP_ANYNL:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- switch(c)
+ GETCHARINC(fc, Feptr);
+ switch(fc)
{
default: RRETURN(MATCH_NOMATCH);
case CHAR_CR:
- if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++;
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
break;
case CHAR_LF:
@@ -4540,49 +2832,49 @@ for (;;)
break;
case OP_NOT_HSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- switch(c)
+ GETCHARINC(fc, Feptr);
+ switch(fc)
{
- HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH);
default: break;
}
}
break;
case OP_HSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- switch(c)
+ GETCHARINC(fc, Feptr);
+ switch(fc)
{
- HSPACE_CASES: break; /* Byte and multibyte cases */
+ HSPACE_CASES: break;
default: RRETURN(MATCH_NOMATCH);
}
}
break;
case OP_NOT_VSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- switch(c)
+ GETCHARINC(fc, Feptr);
+ switch(fc)
{
VSPACE_CASES: RRETURN(MATCH_NOMATCH);
default: break;
@@ -4591,15 +2883,15 @@ for (;;)
break;
case OP_VSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- switch(c)
+ GETCHARINC(fc, Feptr);
+ switch(fc)
{
VSPACE_CASES: break;
default: RRETURN(MATCH_NOMATCH);
@@ -4608,170 +2900,174 @@ for (;;)
break;
case OP_NOT_DIGIT:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINC(c, eptr);
- if (c < 128 && (mb->ctypes[c] & ctype_digit) != 0)
+ GETCHARINC(fc, Feptr);
+ if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0)
RRETURN(MATCH_NOMATCH);
}
break;
case OP_DIGIT:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
uint32_t cc;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- cc = UCHAR21(eptr);
+ cc = UCHAR21(Feptr);
if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
- /* No need to skip more bytes - we know it's a 1-byte character */
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
}
break;
case OP_NOT_WHITESPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
uint32_t cc;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- cc = UCHAR21(eptr);
+ cc = UCHAR21(Feptr);
if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
break;
case OP_WHITESPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
uint32_t cc;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- cc = UCHAR21(eptr);
+ cc = UCHAR21(Feptr);
if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
- /* No need to skip more bytes - we know it's a 1-byte character */
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
}
break;
case OP_NOT_WORDCHAR:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
uint32_t cc;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- cc = UCHAR21(eptr);
+ cc = UCHAR21(Feptr);
if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
break;
case OP_WORDCHAR:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
uint32_t cc;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- cc = UCHAR21(eptr);
+ cc = UCHAR21(Feptr);
if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
- /* No need to skip more bytes - we know it's a 1-byte character */
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
}
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
- } /* End switch(ctype) */
+ return PCRE2_ERROR_INTERNAL;
+ } /* End switch(Lctype) */
else
#endif /* SUPPORT_UNICODE */
- /* Code for the non-UTF-8 case for minimum matching of operators other
+ /* Code for the non-UTF case for minimum matching of operators other
than OP_PROP and OP_NOTPROP. */
- switch(ctype)
+ switch(Lctype)
{
case OP_ANY:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
if (mb->partial != 0 &&
- eptr + 1 >= mb->end_subject &&
+ Feptr + 1 >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- *eptr == NLBLOCK->nl[0])
+ *Feptr == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
- eptr++;
+ Feptr++;
}
break;
case OP_ALLANY:
- if (eptr > mb->end_subject - min)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += min;
- break;
-
- case OP_ANYBYTE:
- if (eptr > mb->end_subject - min)
+ if (Feptr > mb->end_subject - Lmin)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- eptr += min;
+ Feptr += Lmin;
break;
+ /* This OP_ANYBYTE case will never be reached because \C gets turned
+ into OP_ALLANY in non-UTF mode. Cut out the code so that coverage
+ reports don't complain about it's never being used. */
+
+/* case OP_ANYBYTE:
+* if (Feptr > mb->end_subject - Lmin)
+* {
+* SCHECK_PARTIAL();
+* RRETURN(MATCH_NOMATCH);
+* }
+* Feptr += Lmin;
+* break;
+*/
case OP_ANYNL:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- switch(*eptr++)
+ switch(*Feptr++)
{
default: RRETURN(MATCH_NOMATCH);
case CHAR_CR:
- if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++;
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
break;
case CHAR_LF:
@@ -4791,14 +3087,14 @@ for (;;)
break;
case OP_NOT_HSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- switch(*eptr++)
+ switch(*Feptr++)
{
default: break;
HSPACE_BYTE_CASES:
@@ -4811,14 +3107,14 @@ for (;;)
break;
case OP_HSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- switch(*eptr++)
+ switch(*Feptr++)
{
default: RRETURN(MATCH_NOMATCH);
HSPACE_BYTE_CASES:
@@ -4831,14 +3127,14 @@ for (;;)
break;
case OP_NOT_VSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- switch(*eptr++)
+ switch(*Feptr++)
{
VSPACE_BYTE_CASES:
#if PCRE2_CODE_UNIT_WIDTH != 8
@@ -4851,14 +3147,14 @@ for (;;)
break;
case OP_VSPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- switch(*eptr++)
+ switch(*Feptr++)
{
default: RRETURN(MATCH_NOMATCH);
VSPACE_BYTE_CASES:
@@ -4871,212 +3167,212 @@ for (;;)
break;
case OP_NOT_DIGIT:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0)
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
case OP_DIGIT:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0)
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
case OP_NOT_WHITESPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0)
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
case OP_WHITESPACE:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0)
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
case OP_NOT_WORDCHAR:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0)
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
case OP_WORDCHAR:
- for (i = 1; i <= min; i++)
+ for (i = 1; i <= Lmin; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0)
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
RRETURN(MATCH_NOMATCH);
- eptr++;
+ Feptr++;
}
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
}
- /* If min = max, continue at the same level without recursing */
+ /* If Lmin = Lmax we are done. Continue with the main loop. */
- if (min == max) continue;
+ if (Lmin == Lmax) continue;
/* If minimizing, we have to test the rest of the pattern before each
- subsequent match. Again, separate the UTF-8 case for speed, and also
- separate the UCP cases. */
+ subsequent match. */
- if (minimize)
+ if (reptype == REPTYPE_MIN)
{
#ifdef SUPPORT_UNICODE
- if (prop_type >= 0)
+ if (proptype >= 0)
{
- switch(prop_type)
+ switch(proptype)
{
case PT_ANY:
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM36);
+ RMATCH(Fecode, RM208);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(fc, Feptr);
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_LAMP:
- for (fi = min;; fi++)
+ for (;;)
{
int chartype;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM37);
+ RMATCH(Fecode, RM209);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- chartype = UCD_CHARTYPE(c);
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
if ((chartype == ucp_Lu ||
chartype == ucp_Ll ||
- chartype == ucp_Lt) == prop_fail_result)
+ chartype == ucp_Lt) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_GC:
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM38);
+ RMATCH(Fecode, RM210);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_PC:
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM39);
+ RMATCH(Fecode, RM211);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_SC:
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM40);
+ RMATCH(Fecode, RM212);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_ALNUM:
- for (fi = min;; fi++)
+ for (;;)
{
int category;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM59);
+ RMATCH(Fecode, RM213);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- category = UCD_CATEGORY(c);
- if ((category == ucp_L || category == ucp_N) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) ==
+ (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
@@ -5087,26 +3383,26 @@ for (;;)
case PT_SPACE: /* Perl space */
case PT_PXSPACE: /* POSIX space */
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM61);
+ RMATCH(Fecode, RM214);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- switch(c)
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
{
HSPACE_CASES:
VSPACE_CASES:
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
break;
default:
- if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result)
+ if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
break;
}
@@ -5114,105 +3410,101 @@ for (;;)
/* Control never gets here */
case PT_WORD:
- for (fi = min;; fi++)
+ for (;;)
{
int category;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM62);
+ RMATCH(Fecode, RM215);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- category = UCD_CATEGORY(c);
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
if ((category == ucp_L ||
category == ucp_N ||
- c == CHAR_UNDERSCORE)
- == prop_fail_result)
+ fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
case PT_CLIST:
- for (fi = min;; fi++)
+ for (;;)
{
const uint32_t *cp;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM67);
+ RMATCH(Fecode, RM216);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- cp = PRIV(ucd_caseless_sets) + prop_value;
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
for (;;)
{
- if (c < *cp)
- { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } }
- if (c == *cp++)
- { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; }
+ if (fc < *cp)
+ {
+ if (Lctype == OP_NOTPROP) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+ }
}
}
/* Control never gets here */
case PT_UCNC:
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM60);
+ RMATCH(Fecode, RM217);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- GETCHARINCTEST(c, eptr);
- if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
- c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
- c >= 0xe000) == prop_fail_result)
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Lctype == OP_NOTPROP))
RRETURN(MATCH_NOMATCH);
}
/* Control never gets here */
/* This should never occur */
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
}
/* Match extended Unicode sequences. We will get here only if the
support is in the binary; otherwise a compile-time error occurs. */
- else if (ctype == OP_EXTUNI)
+ else if (Lctype == OP_EXTUNI)
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM41);
+ RMATCH(Fecode, RM218);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
else
{
- int lgb, rgb;
- GETCHARINCTEST(c, eptr);
- lgb = UCD_GRAPHBREAK(c);
- while (eptr < mb->end_subject)
- {
- int len = 1;
- if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
- rgb = UCD_GRAPHBREAK(c);
- if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
- lgb = rgb;
- eptr += len;
- }
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
}
CHECK_PARTIAL();
}
@@ -5220,33 +3512,34 @@ for (;;)
else
#endif /* SUPPORT_UNICODE */
+ /* UTF mode for non-property testing character types. */
+
#ifdef SUPPORT_UNICODE
if (utf)
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM42);
+ RMATCH(Fecode, RM219);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (ctype == OP_ANY && IS_NEWLINE(eptr))
- RRETURN(MATCH_NOMATCH);
- GETCHARINC(c, eptr);
- switch(ctype)
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(fc, Feptr);
+ switch(Lctype)
{
case OP_ANY: /* This is the non-NL case */
if (mb->partial != 0 && /* Take care with CRLF partial */
- eptr >= mb->end_subject &&
+ Feptr >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- c == NLBLOCK->nl[0])
+ fc == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
break;
@@ -5255,11 +3548,12 @@ for (;;)
break;
case OP_ANYNL:
- switch(c)
+ switch(fc)
{
default: RRETURN(MATCH_NOMATCH);
+
case CHAR_CR:
- if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++;
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
break;
case CHAR_LF:
@@ -5272,13 +3566,14 @@ for (;;)
case 0x2028:
case 0x2029:
#endif /* Not EBCDIC */
- if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
break;
}
break;
case OP_NOT_HSPACE:
- switch(c)
+ switch(fc)
{
HSPACE_CASES: RRETURN(MATCH_NOMATCH);
default: break;
@@ -5286,7 +3581,7 @@ for (;;)
break;
case OP_HSPACE:
- switch(c)
+ switch(fc)
{
HSPACE_CASES: break;
default: RRETURN(MATCH_NOMATCH);
@@ -5294,7 +3589,7 @@ for (;;)
break;
case OP_NOT_VSPACE:
- switch(c)
+ switch(fc)
{
VSPACE_CASES: RRETURN(MATCH_NOMATCH);
default: break;
@@ -5302,7 +3597,7 @@ for (;;)
break;
case OP_VSPACE:
- switch(c)
+ switch(fc)
{
VSPACE_CASES: break;
default: RRETURN(MATCH_NOMATCH);
@@ -5310,68 +3605,69 @@ for (;;)
break;
case OP_NOT_DIGIT:
- if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0)
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0)
RRETURN(MATCH_NOMATCH);
break;
case OP_DIGIT:
- if (c >= 256 || (mb->ctypes[c] & ctype_digit) == 0)
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0)
RRETURN(MATCH_NOMATCH);
break;
case OP_NOT_WHITESPACE:
- if (c < 256 && (mb->ctypes[c] & ctype_space) != 0)
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0)
RRETURN(MATCH_NOMATCH);
break;
case OP_WHITESPACE:
- if (c >= 256 || (mb->ctypes[c] & ctype_space) == 0)
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0)
RRETURN(MATCH_NOMATCH);
break;
case OP_NOT_WORDCHAR:
- if (c < 256 && (mb->ctypes[c] & ctype_word) != 0)
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0)
RRETURN(MATCH_NOMATCH);
break;
case OP_WORDCHAR:
- if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0)
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0)
RRETURN(MATCH_NOMATCH);
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
}
}
else
-#endif
+#endif /* SUPPORT_UNICODE */
+
/* Not UTF mode */
{
- for (fi = min;; fi++)
+ for (;;)
{
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM43);
+ RMATCH(Fecode, RM33);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= mb->end_subject)
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
RRETURN(MATCH_NOMATCH);
}
- if (ctype == OP_ANY && IS_NEWLINE(eptr))
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr))
RRETURN(MATCH_NOMATCH);
- c = *eptr++;
- switch(ctype)
+ fc = *Feptr++;
+ switch(Lctype)
{
case OP_ANY: /* This is the non-NL case */
if (mb->partial != 0 && /* Take care with CRLF partial */
- eptr >= mb->end_subject &&
+ Feptr >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- c == NLBLOCK->nl[0])
+ fc == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
break;
@@ -5380,11 +3676,12 @@ for (;;)
break;
case OP_ANYNL:
- switch(c)
+ switch(fc)
{
default: RRETURN(MATCH_NOMATCH);
+
case CHAR_CR:
- if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++;
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
break;
case CHAR_LF:
@@ -5397,13 +3694,14 @@ for (;;)
case 0x2028:
case 0x2029:
#endif
- if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
break;
}
break;
case OP_NOT_HSPACE:
- switch(c)
+ switch(fc)
{
default: break;
HSPACE_BYTE_CASES:
@@ -5415,7 +3713,7 @@ for (;;)
break;
case OP_HSPACE:
- switch(c)
+ switch(fc)
{
default: RRETURN(MATCH_NOMATCH);
HSPACE_BYTE_CASES:
@@ -5427,7 +3725,7 @@ for (;;)
break;
case OP_NOT_VSPACE:
- switch(c)
+ switch(fc)
{
default: break;
VSPACE_BYTE_CASES:
@@ -5439,7 +3737,7 @@ for (;;)
break;
case OP_VSPACE:
- switch(c)
+ switch(fc)
{
default: RRETURN(MATCH_NOMATCH);
VSPACE_BYTE_CASES:
@@ -5451,31 +3749,37 @@ for (;;)
break;
case OP_NOT_DIGIT:
- if (MAX_255(c) && (mb->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
break;
case OP_DIGIT:
- if (!MAX_255(c) || (mb->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
break;
case OP_NOT_WHITESPACE:
- if (MAX_255(c) && (mb->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
break;
case OP_WHITESPACE:
- if (!MAX_255(c) || (mb->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
break;
case OP_NOT_WORDCHAR:
- if (MAX_255(c) && (mb->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH);
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
break;
case OP_WORDCHAR:
- if (!MAX_255(c) || (mb->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH);
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
}
}
@@ -5483,113 +3787,116 @@ for (;;)
}
/* If maximizing, it is worth using inline code for speed, doing the type
- test once at the start (i.e. keep it out of the loop). Again, keep the
- UTF-8 and UCP stuff separate. */
+ test once at the start (i.e. keep it out of the loop). */
else
{
- pp = eptr; /* Remember where we started */
+ Lstart_eptr = Feptr; /* Remember where we started */
#ifdef SUPPORT_UNICODE
- if (prop_type >= 0)
+ if (proptype >= 0)
{
- switch(prop_type)
+ switch(proptype)
{
case PT_ANY:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- if (prop_fail_result) break;
- eptr+= len;
+ GETCHARLENTEST(fc, Feptr, len);
+ if (Lctype == OP_NOTPROP) break;
+ Feptr+= len;
}
break;
case PT_LAMP:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int chartype;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- chartype = UCD_CHARTYPE(c);
+ GETCHARLENTEST(fc, Feptr, len);
+ chartype = UCD_CHARTYPE(fc);
if ((chartype == ucp_Lu ||
chartype == ucp_Ll ||
- chartype == ucp_Lt) == prop_fail_result)
+ chartype == ucp_Lt) == (Lctype == OP_NOTPROP))
break;
- eptr+= len;
+ Feptr+= len;
}
break;
case PT_GC:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) break;
- eptr+= len;
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ break;
+ Feptr+= len;
}
break;
case PT_PC:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) break;
- eptr+= len;
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ break;
+ Feptr+= len;
}
break;
case PT_SC:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break;
- eptr+= len;
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ break;
+ Feptr+= len;
}
break;
case PT_ALNUM:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int category;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- category = UCD_CATEGORY(c);
- if ((category == ucp_L || category == ucp_N) == prop_fail_result)
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) ==
+ (Lctype == OP_NOTPROP))
break;
- eptr+= len;
+ Feptr+= len;
}
break;
@@ -5599,186 +3906,178 @@ for (;;)
case PT_SPACE: /* Perl space */
case PT_PXSPACE: /* POSIX space */
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- switch(c)
+ GETCHARLENTEST(fc, Feptr, len);
+ switch(fc)
{
HSPACE_CASES:
VSPACE_CASES:
- if (prop_fail_result) goto ENDLOOP99; /* Break the loop */
+ if (Lctype == OP_NOTPROP) goto ENDLOOP99; /* Break the loop */
break;
default:
- if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result)
+ if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))
goto ENDLOOP99; /* Break the loop */
break;
}
- eptr+= len;
+ Feptr+= len;
}
ENDLOOP99:
break;
case PT_WORD:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int category;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- category = UCD_CATEGORY(c);
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
if ((category == ucp_L || category == ucp_N ||
- c == CHAR_UNDERSCORE) == prop_fail_result)
+ fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP))
break;
- eptr+= len;
+ Feptr+= len;
}
break;
case PT_CLIST:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
const uint32_t *cp;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- cp = PRIV(ucd_caseless_sets) + prop_value;
+ GETCHARLENTEST(fc, Feptr, len);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
for (;;)
{
- if (c < *cp)
- { if (prop_fail_result) break; else goto GOT_MAX; }
- if (c == *cp++)
- { if (prop_fail_result) goto GOT_MAX; else break; }
+ if (fc < *cp)
+ { if (Lctype == OP_NOTPROP) break; else goto GOT_MAX; }
+ if (fc == *cp++)
+ { if (Lctype == OP_NOTPROP) goto GOT_MAX; else break; }
}
- eptr += len;
+ Feptr += len;
}
GOT_MAX:
break;
case PT_UCNC:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLENTEST(c, eptr, len);
- if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
- c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
- c >= 0xe000) == prop_fail_result)
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Lctype == OP_NOTPROP))
break;
- eptr += len;
+ Feptr += len;
}
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
- /* eptr is now past the end of the maximum run */
+ /* Feptr is now past the end of the maximum run */
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
- /* After \C in UTF mode, pp might be in the middle of a Unicode
- character. Use <= pp to ensure backtracking doesn't go too far. */
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= pp to ensure backtracking doesn't go too far.
+ */
for(;;)
{
- if (eptr <= pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM44);
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM222);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- if (utf) BACKCHAR(eptr);
+ Feptr--;
+ if (utf) BACKCHAR(Feptr);
}
}
/* Match extended Unicode grapheme clusters. We will get here only if the
support is in the binary; otherwise a compile-time error occurs. */
- else if (ctype == OP_EXTUNI)
+ else if (Lctype == OP_EXTUNI)
{
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
else
{
- int lgb, rgb;
- GETCHARINCTEST(c, eptr);
- lgb = UCD_GRAPHBREAK(c);
- while (eptr < mb->end_subject)
- {
- int len = 1;
- if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
- rgb = UCD_GRAPHBREAK(c);
- if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
- lgb = rgb;
- eptr += len;
- }
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
}
CHECK_PARTIAL();
}
- /* eptr is now past the end of the maximum run */
+ /* Feptr is now past the end of the maximum run */
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
- /* We use <= pp rather than == pp to detect the start of the run while
- backtracking because the use of \C in UTF mode can cause BACKCHAR to
- move back past pp. This is just palliative; the use of \C in UTF mode
- is fraught with danger. */
+ /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start
+ of the run while backtracking because the use of \C in UTF mode can
+ cause BACKCHAR to move back past Lstart_eptr. This is just palliative;
+ the use of \C in UTF mode is fraught with danger. */
for(;;)
{
int lgb, rgb;
PCRE2_SPTR fptr;
- if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM45);
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ RMATCH(Fecode, RM220);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
/* Backtracking over an extended grapheme cluster involves inspecting
the previous two characters (if present) to see if a break is
permitted between them. */
- eptr--;
- if (!utf) c = *eptr; else
+ Feptr--;
+ if (!utf) fc = *Feptr; else
{
- BACKCHAR(eptr);
- GETCHAR(c, eptr);
+ BACKCHAR(Feptr);
+ GETCHAR(fc, Feptr);
}
- rgb = UCD_GRAPHBREAK(c);
+ rgb = UCD_GRAPHBREAK(fc);
for (;;)
{
- if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */
- fptr = eptr - 1;
- if (!utf) c = *fptr; else
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ fptr = Feptr - 1;
+ if (!utf) fc = *fptr; else
{
BACKCHAR(fptr);
- GETCHAR(c, fptr);
+ GETCHAR(fc, fptr);
}
- lgb = UCD_GRAPHBREAK(c);
+ lgb = UCD_GRAPHBREAK(fc);
if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break;
- eptr = fptr;
+ Feptr = fptr;
rgb = lgb;
}
}
@@ -5790,325 +4089,328 @@ for (;;)
#ifdef SUPPORT_UNICODE
if (utf)
{
- switch(ctype)
+ switch(Lctype)
{
case OP_ANY:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (IS_NEWLINE(eptr)) break;
+ if (IS_NEWLINE(Feptr)) break;
if (mb->partial != 0 && /* Take care with CRLF partial */
- eptr + 1 >= mb->end_subject &&
+ Feptr + 1 >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- UCHAR21(eptr) == NLBLOCK->nl[0])
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
break;
case OP_ALLANY:
- if (max < INT_MAX)
+ if (Lmax < UINT32_MAX)
{
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- eptr++;
- ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
}
}
else
{
- eptr = mb->end_subject; /* Unlimited UTF-8 repeat */
+ Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */
SCHECK_PARTIAL();
}
break;
- /* The byte case is the same as non-UTF8 */
+ /* The "byte" (i.e. "code unit") case is the same as non-UTF */
case OP_ANYBYTE:
- c = max - min;
- if (c > (uint32_t)(mb->end_subject - eptr))
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
{
- eptr = mb->end_subject;
+ Feptr = mb->end_subject;
SCHECK_PARTIAL();
}
- else eptr += c;
+ else Feptr += fc;
break;
case OP_ANYNL:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c == CHAR_CR)
+ GETCHARLEN(fc, Feptr, len);
+ if (fc == CHAR_CR)
{
- if (++eptr >= mb->end_subject) break;
- if (UCHAR21(eptr) == CHAR_LF) eptr++;
+ if (++Feptr >= mb->end_subject) break;
+ if (UCHAR21(Feptr) == CHAR_LF) Feptr++;
}
else
{
- if (c != CHAR_LF &&
+ if (fc != CHAR_LF &&
(mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
- (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
#ifndef EBCDIC
- && c != 0x2028 && c != 0x2029
+ && fc != 0x2028 && fc != 0x2029
#endif /* Not EBCDIC */
)))
break;
- eptr += len;
+ Feptr += len;
}
}
break;
case OP_NOT_HSPACE:
case OP_HSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
BOOL gotspace;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- switch(c)
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
{
HSPACE_CASES: gotspace = TRUE; break;
default: gotspace = FALSE; break;
}
- if (gotspace == (ctype == OP_NOT_HSPACE)) break;
- eptr += len;
+ if (gotspace == (Lctype == OP_NOT_HSPACE)) break;
+ Feptr += len;
}
break;
case OP_NOT_VSPACE:
case OP_VSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
BOOL gotspace;
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- switch(c)
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
{
VSPACE_CASES: gotspace = TRUE; break;
default: gotspace = FALSE; break;
}
- if (gotspace == (ctype == OP_NOT_VSPACE)) break;
- eptr += len;
+ if (gotspace == (Lctype == OP_NOT_VSPACE)) break;
+ Feptr += len;
}
break;
case OP_NOT_DIGIT:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break;
+ Feptr+= len;
}
break;
case OP_DIGIT:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c >= 256 ||(mb->ctypes[c] & ctype_digit) == 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break;
+ Feptr+= len;
}
break;
case OP_NOT_WHITESPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break;
+ Feptr+= len;
}
break;
case OP_WHITESPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c >= 256 ||(mb->ctypes[c] & ctype_space) == 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break;
+ Feptr+= len;
}
break;
case OP_NOT_WORDCHAR:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break;
+ Feptr+= len;
}
break;
case OP_WORDCHAR:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
int len = 1;
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- GETCHARLEN(c, eptr, len);
- if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) break;
- eptr+= len;
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break;
+ Feptr+= len;
}
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
- /* After \C in UTF mode, pp might be in the middle of a Unicode
- character. Use <= pp to ensure backtracking doesn't go too far. */
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go
+ too far. */
for(;;)
{
- if (eptr <= pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM46);
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM221);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- BACKCHAR(eptr);
- if (ctype == OP_ANYNL && eptr > pp && UCHAR21(eptr) == CHAR_NL &&
- UCHAR21(eptr - 1) == CHAR_CR) eptr--;
+ Feptr--;
+ BACKCHAR(Feptr);
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr &&
+ UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR)
+ Feptr--;
}
}
else
#endif /* SUPPORT_UNICODE */
+
/* Not UTF mode */
{
- switch(ctype)
+ switch(Lctype)
{
case OP_ANY:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (IS_NEWLINE(eptr)) break;
+ if (IS_NEWLINE(Feptr)) break;
if (mb->partial != 0 && /* Take care with CRLF partial */
- eptr + 1 >= mb->end_subject &&
+ Feptr + 1 >= mb->end_subject &&
NLBLOCK->nltype == NLTYPE_FIXED &&
NLBLOCK->nllen == 2 &&
- *eptr == NLBLOCK->nl[0])
+ *Feptr == NLBLOCK->nl[0])
{
mb->hitend = TRUE;
- if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL);
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
}
- eptr++;
+ Feptr++;
}
break;
case OP_ALLANY:
case OP_ANYBYTE:
- c = max - min;
- if (c > (uint32_t)(mb->end_subject - eptr))
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
{
- eptr = mb->end_subject;
+ Feptr = mb->end_subject;
SCHECK_PARTIAL();
}
- else eptr += c;
+ else Feptr += fc;
break;
case OP_ANYNL:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- c = *eptr;
- if (c == CHAR_CR)
+ fc = *Feptr;
+ if (fc == CHAR_CR)
{
- if (++eptr >= mb->end_subject) break;
- if (*eptr == CHAR_LF) eptr++;
+ if (++Feptr >= mb->end_subject) break;
+ if (*Feptr == CHAR_LF) Feptr++;
}
else
{
- if (c != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
- (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL
+ if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
#if PCRE2_CODE_UNIT_WIDTH != 8
- && c != 0x2028 && c != 0x2029
+ && fc != 0x2028 && fc != 0x2029
#endif
))) break;
- eptr++;
+ Feptr++;
}
}
break;
case OP_NOT_HSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- switch(*eptr)
+ switch(*Feptr)
{
- default: eptr++; break;
+ default: Feptr++; break;
HSPACE_BYTE_CASES:
#if PCRE2_CODE_UNIT_WIDTH != 8
HSPACE_MULTIBYTE_CASES:
@@ -6120,37 +4422,37 @@ for (;;)
break;
case OP_HSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- switch(*eptr)
+ switch(*Feptr)
{
default: goto ENDLOOP01;
HSPACE_BYTE_CASES:
#if PCRE2_CODE_UNIT_WIDTH != 8
HSPACE_MULTIBYTE_CASES:
#endif
- eptr++; break;
+ Feptr++; break;
}
}
ENDLOOP01:
break;
case OP_NOT_VSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- switch(*eptr)
+ switch(*Feptr)
{
- default: eptr++; break;
+ default: Feptr++; break;
VSPACE_BYTE_CASES:
#if PCRE2_CODE_UNIT_WIDTH != 8
VSPACE_MULTIBYTE_CASES:
@@ -6162,251 +4464,1494 @@ for (;;)
break;
case OP_VSPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- switch(*eptr)
+ switch(*Feptr)
{
default: goto ENDLOOP03;
VSPACE_BYTE_CASES:
#if PCRE2_CODE_UNIT_WIDTH != 8
VSPACE_MULTIBYTE_CASES:
#endif
- eptr++; break;
+ Feptr++; break;
}
}
ENDLOOP03:
break;
case OP_NOT_DIGIT:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) break;
- eptr++;
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
+ break;
+ Feptr++;
}
break;
case OP_DIGIT:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) break;
- eptr++;
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
+ break;
+ Feptr++;
}
break;
case OP_NOT_WHITESPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0) break;
- eptr++;
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
+ break;
+ Feptr++;
}
break;
case OP_WHITESPACE:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0) break;
- eptr++;
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
+ break;
+ Feptr++;
}
break;
case OP_NOT_WORDCHAR:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0) break;
- eptr++;
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
+ break;
+ Feptr++;
}
break;
case OP_WORDCHAR:
- for (i = min; i < max; i++)
+ for (i = Lmin; i < Lmax; i++)
{
- if (eptr >= mb->end_subject)
+ if (Feptr >= mb->end_subject)
{
SCHECK_PARTIAL();
break;
}
- if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0) break;
- eptr++;
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
+ break;
+ Feptr++;
}
break;
default:
- RRETURN(PCRE2_ERROR_INTERNAL);
+ return PCRE2_ERROR_INTERNAL;
}
- if (possessive) continue; /* No backtracking */
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
for (;;)
{
- if (eptr == pp) goto TAIL_RECURSE;
- RMATCH(eptr, ecode, offset_top, mb, eptrb, RM47);
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM34);
if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- if (ctype == OP_ANYNL && eptr > pp && *eptr == CHAR_LF &&
- eptr[-1] == CHAR_CR) eptr--;
+ Feptr--;
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF &&
+ Feptr[-1] == CHAR_CR) Feptr--;
}
}
+ }
+ break; /* End of repeat character type processing */
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lctype
+#undef Lpropvalue
+
+
+ /* ===================================================================== */
+ /* Match a back reference, possibly repeatedly. Look past the end of the
+ item to see if there is repeat information following. The OP_REF and
+ OP_REFI opcodes are used for a reference to a numbered group or to a
+ non-duplicated named group. For a duplicated named group, OP_DNREF and
+ OP_DNREFI are used. In this case we must scan the list of groups to which
+ the name refers, and use the first one that is set. */
+
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lcaseless F->temp_32[2]
+#define Lstart F->temp_sptr[0]
+#define Loffset F->temp_size
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ Lcaseless = (Fop == OP_DNREFI);
+ {
+ int count = GET2(Fecode, 1+IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ Fecode += 1 + 2*IMM2_SIZE;
+
+ while (count-- > 0)
+ {
+ Loffset = (GET2(slot, 0) << 1) - 2;
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ goto REF_REPEAT;
+ case OP_REF:
+ case OP_REFI:
+ Lcaseless = (Fop == OP_REFI);
+ Loffset = (GET2(Fecode, 1) << 1) - 2;
+ Fecode += 1 + IMM2_SIZE;
+
+ /* Set up for repetition, or handle the non-repeated case. The maximum and
+ minimum must be in the heap frame, but as they are short-term values, we
+ use temporary fields. */
+
+ REF_REPEAT:
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ {
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &length);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ Feptr += length;
+ continue; /* With the main loop */
+ }
+
+ /* Handle repeated back references. If a set group has length zero, just
+ continue with the main loop, because it matches however many times. For an
+ unset reference, if the minimum is zero, we can also just continue. We can
+ also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset
+ group behave as a zero-length group. For any other unset cases, carrying
+ on will result in NOMATCH. */
+
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET)
+ {
+ if (Fovector[Loffset] == Fovector[Loffset + 1]) continue;
+ }
+ else /* Group is not set */
+ {
+ if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ continue;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
+
+ /* If min = max, we are done. They are not both allowed to be zero. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep trying and advancing the pointer. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ PCRE2_SIZE slength;
+ RMATCH(Fecode, RM20);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
/* Control never gets here */
}
- /* There's been some horrible disaster. Arrival here can only mean there is
- something seriously wrong in the code above or the OP_xxx definitions. */
+ /* If maximizing, find the longest string and work backwards, as long as
+ the matched lengths for each iteration are the same. */
- default:
- RRETURN(PCRE2_ERROR_INTERNAL);
- }
+ else
+ {
+ BOOL samelengths = TRUE;
+ Lstart = Feptr; /* Starting position */
+ Flength = Fovector[Loffset+1] - Fovector[Loffset];
- /* Do not stick any code in here without much thought; it is assumed
- that "continue" in the code above comes out to here to repeat the main
- loop. */
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ /* Can't use CHECK_PARTIAL because we don't want to update Feptr in
+ the soft partial matching case. */
- } /* End of main loop */
-/* Control never reaches here */
+ if (rrc > 0 && mb->partial != 0 &&
+ mb->end_subject > mb->start_used_ptr)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+ }
+ if (slength != Flength) samelengths = FALSE;
+ Feptr += slength;
+ }
-/* When compiling to use the heap rather than the stack for recursive calls to
-match(), the RRETURN() macro jumps here. The number that is saved in
-frame->Xwhere indicates which label we actually want to return to. */
+ /* If the length matched for each repetition is the same as the length of
+ the captured group, we can easily work backwards. This is the normal
+ case. However, in caseless UTF-8 mode there are pairs of case-equivalent
+ characters whose lengths (in terms of code units) differ. However, this
+ is very rare, so we handle it by re-matching fewer and fewer times. */
-#ifdef HEAP_MATCH_RECURSE
-#define LBL(val) case val: goto L_RM##val;
-HEAP_RETURN:
-switch (frame->Xwhere)
- {
- LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)
- LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17)
- LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33)
- LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52)
- LBL(53) LBL(54) LBL(55) LBL(56) LBL(57) LBL(58) LBL(63) LBL(64)
- LBL(65) LBL(66) LBL(68)
-#ifdef SUPPORT_WIDE_CHARS
- LBL(20) LBL(21)
+ if (samelengths)
+ {
+ while (Feptr >= Lstart)
+ {
+ RMATCH(Fecode, RM21);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr -= Flength;
+ }
+ }
+
+ /* The rare case of non-matching lengths. Re-scan the repetition for each
+ iteration. We know that match_ref() will succeed every time. */
+
+ else
+ {
+ Lmax = i;
+ for (;;)
+ {
+ RMATCH(Fecode, RM22);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr == Lstart) break; /* Failed after minimal repetition */
+ Feptr = Lstart;
+ Lmax--;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ (void)match_ref(Loffset, Lcaseless, F, mb, &slength);
+ Feptr += slength;
+ }
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+#undef Lcaseless
+#undef Lmin
+#undef Lmax
+#undef Lstart
+#undef Loffset
+
+
+
+/* ========================================================================= */
+/* Opcodes for the start of various parenthesized items */
+/* ========================================================================= */
+
+ /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the
+ (*THEN) is within the current branch by comparing the address of OP_THEN
+ that is passed back with the end of the branch. If (*THEN) is within the
+ current branch, and the branch is one of two or more alternatives (it
+ either starts or ends with OP_ALT), we have reached the limit of THEN's
+ action, so convert the return code to NOMATCH, which will cause normal
+ backtracking to happen from now on. Otherwise, THEN is passed back to an
+ outer alternative. This implements Perl's treatment of parenthesized
+ groups, where a group not containing | does not affect the current
+ alternative, that is, (X) is NOT the same as (X|(*F)). */
+
+
+ /* ===================================================================== */
+ /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive
+ bracket group, indicating that it may occur zero times. It may repeat
+ infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in
+ the pattern. Brackets with fixed upper repeat limits are compiled as a
+ number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO.
+ Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */
+
+#define Lnext_ecode F->temp_sptr[0]
+
+ case OP_BRAZERO:
+ Lnext_ecode = Fecode + 1;
+ RMATCH(Lnext_ecode, RM9);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ Fecode = Lnext_ecode + 1 + LINK_SIZE;
+ break;
+
+ case OP_BRAMINZERO:
+ Lnext_ecode = Fecode + 1;
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode++;
+ break;
+
+#undef Lnext_ecode
+
+ case OP_SKIPZERO:
+ Fecode++;
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Handle possessive brackets with an unlimited repeat. The end of these
+ brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without
+ going further in the pattern. */
+
+#define Lframe_type F->temp_32[0]
+#define Lmatched_once F->temp_32[1]
+#define Lzero_allowed F->temp_32[2]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lstart_group F->temp_sptr[1]
+
+ case OP_BRAPOSZERO:
+ Lzero_allowed = TRUE; /* Zero repeat is allowed */
+ Fecode += 1;
+ if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS)
+ goto POSSESSIVE_CAPTURE;
+ goto POSSESSIVE_NON_CAPTURE;
+
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_NON_CAPTURE:
+ Lframe_type = GF_NOCAPTURE; /* Remembered frame type */
+ goto POSSESSIVE_GROUP;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_CAPTURE:
+ number = GET2(Fecode, 1+LINK_SIZE);
+ Lframe_type = GF_CAPTURE | number; /* Remembered frame type */
+
+ POSSESSIVE_GROUP:
+ Lmatched_once = FALSE; /* Never matched */
+ Lstart_group = Fecode; /* Start of this group */
+
+ for (;;)
+ {
+ Lstart_eptr = Feptr; /* Position at group start */
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8);
+ if (rrc == MATCH_KETRPOS)
+ {
+ Lmatched_once = TRUE; /* Matched at least once */
+ if (Feptr == Lstart_eptr) /* Empty match; skip to end */
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ break;
+ }
+
+ Fecode = Lstart_group;
+ continue;
+ }
+
+ /* See comment above about handling THEN. */
+
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) break;
+ }
+
+ /* Success if matched something or zero repeat allowed */
+
+ if (Lmatched_once || Lzero_allowed)
+ {
+ Fecode += 1 + LINK_SIZE;
+ break;
+ }
+
+ RRETURN(MATCH_NOMATCH);
+
+#undef Lmatched_once
+#undef Lzero_allowed
+#undef Lframe_type
+#undef Lstart_eptr
+#undef Lstart_group
+
+
+ /* ===================================================================== */
+ /* Handle non-capturing brackets that cannot match an empty string. When we
+ get to the final alternative within the brackets, as long as there are no
+ THEN's in the pattern, we can optimize by not recording a new backtracking
+ point. (Ideally we should test for a THEN within this group, but we don't
+ have that information.) Don't do this if we are at the very top level,
+ however, because that would make handling assertions and once-only brackets
+ messier when there is nothing to go back to. */
+
+#define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */
+#define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */
+
+ case OP_BRA:
+ if (mb->hasthen || Frdepth == 0)
+ {
+ Lframe_type = 0;
+ goto GROUPLOOP;
+ }
+
+ for (;;)
+ {
+ Lnext_branch = Fecode + GET(Fecode, 1);
+ if (*Lnext_branch != OP_ALT) break;
+
+ /* This is never the final branch. We do not need to test for MATCH_THEN
+ here because this code is not used when there is a THEN in the pattern. */
+
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode = Lnext_branch;
+ }
+
+ /* Hit the start of the final branch. Continue at this level. */
+
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
+
+#undef Lnext_branch
+
+
+ /* ===================================================================== */
+ /* Handle a capturing bracket, other than those that are possessive with an
+ unlimited repeat. */
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE);
+ goto GROUPLOOP;
+
+
+ /* ===================================================================== */
+ /* Atomic groups and non-capturing brackets that can match an empty string
+ must record a backtracking point and also set up a chained frame. */
+
+ case OP_ONCE:
+ case OP_SBRA:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ GROUPLOOP:
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2);
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Recursion either matches the current regex, or some subexpression. The
+ offset data is the offset to the starting bracket from the start of the
+ whole pattern. (This is so that it works from duplicated subpatterns.) */
+
+#define Lframe_type F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ case OP_RECURSE:
+ bracode = mb->start_code + GET(Fecode, 1);
+ number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE);
+
+ /* If we are already in a recursion, check for repeating the same one
+ without advancing the subject pointer. This should catch convoluted mutual
+ recursions. (Some simple cases are caught at compile time.) */
+
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ offset = Flast_group_offset;
+ while (offset != PCRE2_UNSET)
+ {
+ N = (heapframe *)((char *)mb->match_frames + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_RECURSE | number))
+ {
+ if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP;
+ break;
+ }
+ offset = P->last_group_offset;
+ }
+ }
+
+ /* Now run the recursion, branch by branch. */
+
+ Lstart_branch = bracode;
+ Lframe_type = GF_RECURSE | number;
+
+ for (;;)
+ {
+ PCRE2_SPTR next_ecode;
+
+ group_frame_type = Lframe_type;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11);
+ next_ecode = Lstart_branch + GET(Lstart_branch,1);
+
+ /* Handle backtracking verbs, which are defined in a range that can
+ easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to
+ escape beyond a recursion; they cause a NOMATCH for the entire recursion.
+
+ When one of these verbs triggers, the current recursion group number is
+ recorded. If it matches the recursion we are processing, the verb
+ happened within the recursion and we must deal with it. Otherwise it must
+ have happened after the recursion completed, and so has to be passed
+ back. See comment above about handling THEN. */
+
+ if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX &&
+ mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE))
+ {
+ if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode &&
+ (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ else RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Note that carrying on after (*ACCEPT) in a recursion is handled in the
+ OP_ACCEPT code. Nothing needs to be done here. */
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Lstart_branch = next_ecode;
+ if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+#undef Lstart_branch
+
+
+ /* ===================================================================== */
+ /* Positive assertions are like other groups except that PCRE doesn't allow
+ the effect of (*THEN) to escape beyond an assertion; it is therefore
+ treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its
+ captures retained. Any other return is an error. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ Lframe_type = GF_NOCAPTURE | Fop;
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3);
+ if (rrc == MATCH_ACCEPT)
+ {
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+ break;
+ }
+ if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Handle negative assertions. Loop for each non-matching branch as for
+ positive assertions. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4);
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */
+ case MATCH_MATCH:
+ RRETURN (MATCH_NOMATCH);
+
+ case MATCH_NOMATCH: /* Branch failed, try next if present. */
+ case MATCH_THEN:
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED;
+ break;
+
+ case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ goto ASSERT_NOT_FAILED;
+
+ default: /* Pass back any other return */
+ RRETURN(rrc);
+ }
+ }
+
+ /* None of the branches have matched or there was a backtrack to (*COMMIT),
+ (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a
+ negative assertion, so carry on. */
+
+ ASSERT_NOT_FAILED:
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* The callout item calls an external function, if one is provided, passing
+ details of the match so far. This is mainly for debugging, though the
+ function is able to force a failure. */
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+ Fecode += length;
+ break;
+
+
+ /* ===================================================================== */
+ /* Conditional group: compilation checked that there are no more than two
+ branches. If the condition is false, skipping the first branch takes us
+ past the end of the item if there is only one branch, but that's exactly
+ what we want. */
+
+ case OP_COND:
+ case OP_SCOND:
+
+ /* The variable Flength will be added to Fecode when the condition is
+ false, to get to the second branch. Setting it to the offset to the ALT or
+ KET, then incrementing Fecode achieves this effect. However, if the second
+ branch is non-existent, we must point to the KET so that the end of the
+ group is correctly processed. We now have Fecode pointing to the condition
+ or callout. */
+
+ Flength = GET(Fecode, 1); /* Offset to the second branch */
+ if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE;
+ Fecode += 1 + LINK_SIZE; /* From this opcode */
+
+ /* Because of the way auto-callout works during compile, a callout item is
+ inserted between OP_COND and an assertion condition. Such a callout can
+ also be inserted manually. */
+
+ if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR)
+ {
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+
+ /* Advance Fecode past the callout, so it now points to the condition. We
+ must adjust Flength so that the value of Fecode+Flength is unchanged. */
+
+ Fecode += length;
+ Flength -= length;
+ }
+
+ /* Test the various possible conditions */
+
+ condition = FALSE;
+ switch(*Fecode)
+ {
+ case OP_RREF: /* Group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ number = GET2(Fecode, 1);
+ condition = (number == RREF_ANY || number == Fcurrent_recurse);
+ }
+ break;
+
+ case OP_DNRREF: /* Duplicate named group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ number = GET2(slot, 0);
+ condition = number == Fcurrent_recurse;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_CREF: /* Numbered group used test */
+ offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ break;
+
+ case OP_DNCREF: /* Duplicate named group used test */
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ offset = (GET2(slot, 0) << 1) - 2;
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_FALSE:
+ case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */
+ break;
+
+ case OP_TRUE:
+ condition = TRUE;
+ break;
+
+ /* The condition is an assertion. Run code similar to the assertion code
+ above. */
+
+#define Lpositive F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ default:
+ Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK);
+ Lstart_branch = Fecode;
+
+ for (;;)
+ {
+ group_frame_type = GF_CONDASSERT | *Fecode;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5);
+
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Save captures */
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+
+ /* Fall through */
+ /* In the case of a match, the captures have already been put into
+ the current frame. */
+
+ case MATCH_MATCH:
+ condition = Lpositive; /* TRUE for positive assertion */
+ break;
+
+ /* PCRE doesn't allow the effect of (*THEN) to escape beyond an
+ assertion; it is therefore always treated as NOMATCH. */
+
+ case MATCH_NOMATCH:
+ case MATCH_THEN:
+ Lstart_branch += GET(Lstart_branch, 1);
+ if (*Lstart_branch == OP_ALT) continue; /* Try next branch */
+ condition = !Lpositive; /* TRUE for negative assertion */
+ break;
+
+ /* These force no match without checking other branches. */
+
+ case MATCH_COMMIT:
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ condition = !Lpositive;
+ break;
+
+ default:
+ RRETURN(rrc);
+ }
+ break; /* Out of the branch loop */
+ }
+
+ /* If the condition is true, find the end of the assertion so that
+ advancing past it gets us to the start of the first branch. */
+
+ if (condition)
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ }
+ break; /* End of assertion condition */
+ }
+
+#undef Lpositive
+#undef Lstart_branch
+
+ /* Choose branch according to the condition. */
+
+ Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength;
+
+ /* If the opcode is OP_SCOND it means we are at a repeated conditional
+ group that might match an empty string. We must therefore descend a level
+ so that the start is remembered for checking. For OP_COND we can just
+ continue at this level. */
+
+ if (Fop == OP_SCOND)
+ {
+ group_frame_type = GF_NOCAPTURE | Fop;
+ RMATCH(Fecode, RM35);
+ RRETURN(rrc);
+ }
+ break;
+
+
+
+/* ========================================================================= */
+/* End of start of parenthesis opcodes */
+/* ========================================================================= */
+
+
+ /* ===================================================================== */
+ /* Move the subject pointer back. This occurs only at the start of each
+ branch of a lookbehind assertion. If we are too close to the start to move
+ back, fail. When working with UTF-8 we move back a number of characters,
+ not bytes. */
+
+ case OP_REVERSE:
+ number = GET(Fecode, 1);
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (number-- > 0)
+ {
+ if (Feptr <= mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+
+ /* No UTF-8 support, or not in UTF-8 mode: count is byte count */
+
+ {
+ if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Feptr -= number;
+ }
+
+ /* Save the earliest consulted character, then skip to next op code */
+
+ if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr;
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* An alternation is the end of a branch; scan along to find the end of the
+ bracketed group. */
+
+ case OP_ALT:
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ break;
+
+
+ /* ===================================================================== */
+ /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the
+ starting frame was added to the chained frames in order to remember the
+ starting subject position for the group. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ case OP_KETRPOS:
+
+ bracode = Fecode - GET(Fecode, 1);
+
+ /* Point N to the frame at the start of the most recent group.
+ Remember the subject pointer at the start of the group. */
+
+ if (*bracode != OP_BRA && *bracode != OP_COND)
+ {
+ N = (heapframe *)((char *)mb->match_frames + Flast_group_offset);
+ P = (heapframe *)((char *)N - frame_size);
+ Flast_group_offset = P->last_group_offset;
+
+#ifdef DEBUG_SHOW_RMATCH
+ fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n",
+ N->rdepth, N->group_frame_type,
+ (char *)P->eptr - (char *)mb->start_subject);
#endif
+
+ /* If we are at the end of an assertion that is a condition, return a
+ match, discarding any intermediate backtracking points. Copy back the
+ captures into the frame before N so that they are set on return. Doing
+ this for all assertions, both positive and negative, seems to match what
+ Perl does. */
+
+ if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT)
+ {
+ memcpy((char *)P + offsetof(heapframe, ovector), Fovector,
+ Foffset_top * sizeof(PCRE2_SIZE));
+ P->offset_top = Foffset_top;
+ Fback_frame = (char *)F - (char *)P;
+ RRETURN(MATCH_MATCH);
+ }
+ }
+ else P = NULL; /* Indicates starting frame not recorded */
+
+ /* The group was not a conditional assertion. */
+
+ switch (*bracode)
+ {
+ case OP_BRA: /* No need to do anything for these */
+ case OP_COND:
+ case OP_SCOND:
+ break;
+
+ /* Positive assertions are like OP_ONCE, except that in addition the
+ subject pointer must be put back to where it was at the start of the
+ assertion. */
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ Feptr = P->eptr;
+ /* Fall through */
+
+ /* For an atomic group, discard internal backtracking points. We must
+ also ensure that any remaining branches within the top-level of the group
+ are not tried. Do this by adjusting the code pointer within the backtrack
+ frame so that it points to the final branch. */
+
+ case OP_ONCE:
+ Fback_frame = ((char *)F - (char *)P) + frame_size;
+ for (;;)
+ {
+ uint32_t y = GET(P->ecode,1);
+ if ((P->ecode)[y] != OP_ALT) break;
+ P->ecode += y;
+ }
+ break;
+
+ /* A matching negative assertion returns MATCH, which is turned into
+ NOMATCH at the assertion level. */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ RRETURN(MATCH_MATCH);
+
+ /* Whole-pattern recursion is coded as a recurse into group 0, so it
+ won't be picked up here. Instead, we catch it when the OP_END is reached.
+ Other recursion is handled here. */
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ number = GET2(bracode, 1+LINK_SIZE);
+
+ /* Handle a recursively called group. We reinstate the previous set of
+ captures and then carry on after the recursion call. */
+
+ if (Fcurrent_recurse == number)
+ {
+ P = (heapframe *)((char *)N - frame_size);
+ memcpy((char *)F + offsetof(heapframe, ovector), P->ovector,
+ P->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = P->offset_top;
+ Fcapture_last = P->capture_last;
+ Fcurrent_recurse = P->current_recurse;
+ Fecode = P->ecode + 1 + LINK_SIZE;
+ continue; /* With next opcode */
+ }
+
+ /* Deal with actual capturing. */
+
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
+ break;
+ } /* End actions relating to the starting opcode */
+
+ /* OP_KETRPOS is a possessive repeating ket. Remember the current position,
+ and return the MATCH_KETRPOS. This makes it possible to do the repeats one
+ at a time from the outer level. This must precede the empty string test -
+ in this case that test is done at the outer level. */
+
+ if (*Fecode == OP_KETRPOS)
+ {
+ memcpy((char *)P + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+ RRETURN(MATCH_KETRPOS);
+ }
+
+ /* Handle the different kinds of closing brackets. A non-repeating ket
+ needs no special action, just continuing at this level. This also happens
+ for the repeating kets if the group matched no characters, in order to
+ forcibly break infinite loops. Otherwise, the repeating kets try the rest
+ of the pattern or restart from the preceding bracket, in the appropriate
+ order. */
+
+ if (Fop != OP_KET && (P == NULL || Feptr != P->eptr))
+ {
+ if (Fop == OP_KETRMIN)
+ {
+ RMATCH(Fecode + 1 + LINK_SIZE, RM6);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode -= GET(Fecode, 1);
+ break; /* End of ket processing */
+ }
+
+ /* Repeat the maximum number of times (KETRMAX) */
+
+ RMATCH(bracode, RM7);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+
+ /* Carry on at this level for a non-repeating ket, or after matching an
+ empty string, or after repeating for a maximum number of times. */
+
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, not multiline mode. */
+
+ case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */
+ if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_SOD: /* Unconditional start of subject */
+ if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* When PCRE2_NOTEOL is unset, assert before the subject end, or a
+ terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */
+
+ case OP_DOLL:
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS;
+
+ /* Fall through */
+ /* Unconditional end of subject assertion (\z) */
+
+ case OP_EOD:
+ if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH);
+ SCHECK_PARTIAL();
+ Fecode++;
+ break;
+
+ /* End of subject or ending \n assertion (\Z) */
+
+ case OP_EODN:
+ ASSERT_NL_OR_EOS:
+ if (Feptr < mb->end_subject &&
+ (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Either at end of string or \n before end. */
+
+ SCHECK_PARTIAL();
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, multiline mode. */
+
+ /* Start of subject unless notbol, or after any newline except for one at
+ the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */
+
+ case OP_CIRCM:
+ if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject)
+ RRETURN(MATCH_NOMATCH);
+ if (Feptr != mb->start_subject &&
+ ((Feptr == mb->end_subject &&
+ (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) ||
+ !WAS_NEWLINE(Feptr)))
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* Assert before any newline, or before end of subject unless noteol is
+ set. */
+
+ case OP_DOLLM:
+ if (Feptr < mb->end_subject)
+ {
+ if (!IS_NEWLINE(Feptr))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+ {
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ SCHECK_PARTIAL();
+ }
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start of match assertion */
+
+ case OP_SOM:
+ if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Reset the start of match point */
+
+ case OP_SET_SOM:
+ Fstart_match = Feptr;
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Word boundary assertions. Find out if the previous and current
+ characters are "word" characters. It takes a bit more work in UTF mode.
+ Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is
+ not set. When it is set, use Unicode properties if available, even when not
+ in UTF mode. Remember the earliest and latest consulted characters. */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ if (Feptr == mb->start_subject) prev_is_word = FALSE; else
+ {
+ PCRE2_SPTR lastptr = Feptr - 1;
#ifdef SUPPORT_UNICODE
- LBL(16) LBL(18)
- LBL(22) LBL(23) LBL(28) LBL(30)
- LBL(32) LBL(34) LBL(42) LBL(46)
- LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45)
- LBL(59) LBL(60) LBL(61) LBL(62) LBL(67)
+ if (utf)
+ {
+ BACKCHAR(lastptr);
+ GETCHAR(fc, lastptr);
+ }
+ else
#endif /* SUPPORT_UNICODE */
- default:
- return PCRE2_ERROR_INTERNAL;
- }
-#undef LBL
-#endif /* HEAP_MATCH_RECURSE */
-}
+ fc = *lastptr;
+ if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') prev_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ prev_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
+ /* Get status of next character */
-/***************************************************************************
-****************************************************************************
- RECURSION IN THE match() FUNCTION
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ cur_is_word = FALSE;
+ }
+ else
+ {
+ PCRE2_SPTR nextptr = Feptr + 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ FORWARDCHARTEST(nextptr, mb->end_subject);
+ GETCHAR(fc, Feptr);
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ fc = *Feptr;
+ if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') cur_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ cur_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
-Undefine all the macros that were defined above to handle this. */
+ /* Now see if the situation is what we want */
-#ifdef HEAP_MATCH_RECURSE
-#undef eptr
-#undef ecode
-#undef mstart
-#undef offset_top
-#undef eptrb
-#undef flags
+ if ((*Fecode++ == OP_WORD_BOUNDARY)?
+ cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+ RRETURN(MATCH_NOMATCH);
+ break;
-#undef callpat
-#undef charptr
-#undef data
-#undef next_ecode
-#undef pp
-#undef prev
-#undef saved_eptr
-#undef new_recursive
+ /* ===================================================================== */
+ /* Backtracking (*VERB)s, with and without arguments. Note that if the
+ pattern is successfully matched, we do not come back from RMATCH. */
-#undef cur_is_word
-#undef condition
-#undef prev_is_word
+ case OP_MARK:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12);
-#undef ctype
-#undef length
-#undef max
-#undef min
-#undef number
-#undef offset
-#undef op
-#undef save_capture_last
-#undef save_offset1
-#undef save_offset2
-#undef save_offset3
+ /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an
+ argument, and we must check whether that argument matches this MARK's
+ argument. It is passed back in mb->verb_skip_ptr. If it does match, we
+ return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject
+ position that corresponds to this mark. Otherwise, pass back the return
+ code unaltered. */
-#undef newptrb
-#endif /* HEAP_MATCH_RECURSE */
+ if (rrc == MATCH_SKIP_ARG &&
+ PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0)
+ {
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ RRETURN(MATCH_SKIP);
+ }
+ RRETURN(rrc);
-/* These two are defined as macros in both cases */
+ case OP_FAIL:
+ RRETURN(MATCH_NOMATCH);
-#undef fc
-#undef fi
+ /* Record the current recursing group number in mb->verb_current_recurse
+ when a backtracking return such as MATCH_COMMIT is given. This enables the
+ recurse processing to catch verbs from within the recursion. */
-/***************************************************************************
-***************************************************************************/
+ case OP_COMMIT:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_COMMIT);
+ case OP_PRUNE:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
-#ifdef HEAP_MATCH_RECURSE
-/*************************************************
-* Release allocated heap frames *
-*************************************************/
+ case OP_PRUNE_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
-/* This function releases all the allocated frames. The base frame is on the
-machine stack, and so must not be freed.
+ case OP_SKIP:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP);
-Argument:
- frame_base the address of the base frame
- mb the match block
+ /* Note that, for Perl compatibility, SKIP with an argument does NOT set
+ nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was
+ not a matching mark, we have to re-run the match, ignoring the SKIP_ARG
+ that failed and any that precede it (either they also failed, or were not
+ triggered). To do this, we maintain a count of executed SKIP_ARGs. If a
+ SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg
+ set to the count of the one that failed. */
-Returns: nothing
-*/
+ case OP_SKIP_ARG:
+ mb->skip_arg_count++;
+ if (mb->skip_arg_count <= mb->ignore_skip_arg)
+ {
+ Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1];
+ break;
+ }
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
-static void
-release_match_heapframes (heapframe *frame_base, match_block *mb)
-{
-heapframe *nextframe = frame_base->Xnextframe;
-while (nextframe != NULL)
+ /* Pass back the current skip name and return the special MATCH_SKIP_ARG
+ return code. This will either be caught by a matching MARK, or get to the
+ top, where it causes a rematch with mb->ignore_skip_arg set to the value of
+ mb->skip_arg_count. */
+
+ mb->verb_skip_ptr = Fecode + 2;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP_ARG);
+
+ /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that
+ the branch in which it occurs can be determined. */
+
+ case OP_THEN:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+ case OP_THEN_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+
+ /* ===================================================================== */
+ /* There's been some horrible disaster. Arrival here can only mean there is
+ something seriously wrong in the code above or the OP_xxx definitions. */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ /* Do not insert any code in here without much thought; it is assumed
+ that "continue" in the code above comes out to here to repeat the main
+ loop. */
+
+ } /* End of main loop */
+/* Control never reaches here */
+
+
+/* ========================================================================= */
+/* The RRETURN() macro jumps here. The number that is saved in Freturn_id
+indicates which label we actually want to return to. The value in Frdepth is
+the index number of the frame in the vector. The return value has been placed
+in rrc. */
+
+#define LBL(val) case val: goto L_RM##val;
+
+RETURN_SWITCH:
+if (Frdepth == 0) return rrc; /* Exit from the top level */
+F = (heapframe *)((char *)F - Fback_frame); /* Back track */
+mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */
+
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id);
+#endif
+
+switch (Freturn_id)
{
- heapframe *oldframe = nextframe;
- nextframe = nextframe->Xnextframe;
- mb->stack_memctl.free(oldframe, mb->stack_memctl.memory_data);
+ LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)
+ LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16)
+ LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24)
+ LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32)
+ LBL(33) LBL(34) LBL(35)
+
+#ifdef SUPPORT_WIDE_CHARS
+ LBL(100) LBL(101)
+#endif
+
+#ifdef SUPPORT_UNICODE
+ LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206)
+ LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213)
+ LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220)
+ LBL(221) LBL(222)
+#endif
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
}
+#undef LBL
}
-#endif /* HEAP_MATCH_RECURSE */
-
/*************************************************
@@ -6439,8 +5984,6 @@ pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
pcre2_match_context *mcontext)
{
int rc;
-int ocount;
-
const uint8_t *start_bits = NULL;
const pcre2_real_code *re = (const pcre2_real_code *)code;
@@ -6450,7 +5993,6 @@ BOOL firstline;
BOOL has_first_cu = FALSE;
BOOL has_req_cu = FALSE;
BOOL startline;
-BOOL using_temporary_offsets = FALSE;
BOOL utf;
PCRE2_UCHAR first_cu = 0;
@@ -6465,18 +6007,22 @@ PCRE2_SPTR req_cu_ptr = start_match - 1;
PCRE2_SPTR start_partial = NULL;
PCRE2_SPTR match_partial = NULL;
-/* We need to have mb pointing to a match block, because the IS_NEWLINE macro
-is used below, and it expects NLBLOCK to be defined as a pointer. */
+PCRE2_SIZE frame_size;
+
+/* We need to have mb as a pointer to a match block, because the IS_NEWLINE
+macro is used below, and it expects NLBLOCK to be defined as a pointer. */
+pcre2_callout_block cb;
match_block actual_match_block;
match_block *mb = &actual_match_block;
-#ifdef HEAP_MATCH_RECURSE
-heapframe frame_zero;
-frame_zero.Xprevframe = NULL; /* Marks the top level */
-frame_zero.Xnextframe = NULL; /* None are allocated yet */
-mb->match_frames_base = &frame_zero;
-#endif
+/* Allocate an initial vector of backtracking frames on the stack. If this
+proves to be too small, it is replaced by a larger one on the heap. To get a
+vector of the size required that is aligned for pointers, allocate it as a
+vector of pointers. */
+
+PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)];
+mb->stack_frames = (heapframe *)stack_frames_vector;
/* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated
subject string. */
@@ -6505,8 +6051,8 @@ options variable for this function. Users of PCRE2 who are not calling the
function directly would like to have a way of setting these flags, in the same
way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with
constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and
-(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be
-transferred to the options for this function. The bits are guaranteed to be
+(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now
+transfer to the options for this function. The bits are guaranteed to be
adjacent, but do not have the same values. This bit of Boolean trickery assumes
that the match-time bits are not more significant than the flag bits. If by
accident this is not the case, a compile-time division by zero error will
@@ -6518,20 +6064,22 @@ options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));
#undef FF
#undef OO
-/* A NULL match context means "use a default context" */
-
-if (mcontext == NULL)
- mcontext = (pcre2_match_context *)(&PRIV(default_match_context));
-
/* These two settings are used in the code for checking a UTF string that
follows immediately afterwards. Other values in the mb block are used only
-during interpretive pcre_match() processing, not when the JIT support is in
-use, so they are set up later. */
+during interpretive processing, not when the JIT support is in use, so they are
+set up later. */
utf = (re->overall_options & PCRE2_UTF) != 0;
mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 :
((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0;
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if (mb->partial != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
/* Check a UTF string for validity if required. For 8-bit and 16-bit strings,
we must also check that a starting offset does not point into the middle of a
multiunit character. We check only the portion of the subject that is going to
@@ -6590,7 +6138,7 @@ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
/* It is an error to set an offset limit without setting the flag at compile
time. */
-if (mcontext->offset_limit != PCRE2_UNSET &&
+if (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET &&
(re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)
return PCRE2_ERROR_BADOFFSETLIMIT;
@@ -6609,7 +6157,15 @@ if (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0)
}
#endif
-/* Carry on with non-JIT matching. */
+/* Carry on with non-JIT matching. A NULL match context means "use a default
+context", but we take the memory control functions from the pattern. */
+
+if (mcontext == NULL)
+ {
+ mcontext = (pcre2_match_context *)(&PRIV(default_match_context));
+ mb->memctl = re->memctl;
+ }
+else mb->memctl = mcontext->memctl;
anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0;
firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
@@ -6617,14 +6173,19 @@ startline = (re->flags & PCRE2_STARTLINE) != 0;
bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)?
end_subject : subject + mcontext->offset_limit;
-/* Fill in the fields in the match block. */
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+
+/* Fill in the remaining fields in the match block. */
mb->callout = mcontext->callout;
mb->callout_data = mcontext->callout_data;
-mb->memctl = mcontext->memctl;
-#ifdef HEAP_MATCH_RECURSE
-mb->stack_memctl = mcontext->stack_memctl;
-#endif
mb->start_subject = subject;
mb->start_offset = start_offset;
@@ -6636,8 +6197,6 @@ mb->poptions = re->overall_options; /* Pattern options */
mb->ignore_skip_arg = 0;
mb->mark = mb->nomatch_mark = NULL; /* In case never set */
-mb->recursive = NULL; /* No recursion at top level */
-mb->ovecsave_chain = NULL; /* No ovecsave blocks yet */
mb->hitend = FALSE;
/* The name table is needed for finding all the numbers associated with a
@@ -6648,20 +6207,6 @@ mb->name_count = re->name_count;
mb->name_entry_size = re->name_entry_size;
mb->start_code = mb->name_table + re->name_count * re->name_entry_size;
-/* Limits set in the pattern override the match context only if they are
-smaller. */
-
-mb->match_limit = (mcontext->match_limit < re->limit_match)?
- mcontext->match_limit : re->limit_match;
-mb->match_limit_recursion = (mcontext->recursion_limit < re->limit_recursion)?
- mcontext->recursion_limit : re->limit_recursion;
-
-/* Pointers to the individual character tables */
-
-mb->lcc = re->tables + lcc_offset;
-mb->fcc = re->tables + fcc_offset;
-mb->ctypes = re->tables + ctypes_offset;
-
/* Process the \R and newline settings. */
mb->bsr_convention = re->bsr_convention;
@@ -6678,6 +6223,11 @@ switch(re->newline_convention)
mb->nl[0] = CHAR_NL;
break;
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
case PCRE2_NEWLINE_CRLF:
mb->nllen = 2;
mb->nl[0] = CHAR_CR;
@@ -6695,71 +6245,91 @@ switch(re->newline_convention)
default: return PCRE2_ERROR_INTERNAL;
}
-/* If the expression has got more back references than the offsets supplied can
-hold, we get a temporary chunk of memory to use during the matching. Otherwise,
-we can use the vector supplied. The size of the ovector is three times the
-value in the oveccount field. Two-thirds of it is pairs for storing matching
-offsets, and the top third is working space. */
+/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE
+vector at the end, whose size depends on the number of capturing parentheses in
+the pattern. It is not used at all if there are no capturing parentheses.
+
+ frame_size is the total size of each frame
+ mb->frame_vector_size is the total usable size of the vector (rounded down
+ to a whole number of frames)
-if (re->top_backref >= match_data->oveccount)
+The last of these is changed within the match() function if the frame vector
+has to be expanded. We therefore put it into the match block so that it is
+correct when calling match() more than once for non-anchored patterns. */
+
+frame_size = offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE);
+
+/* Limits set in the pattern override the match context only if they are
+smaller. */
+
+mb->heap_limit = (mcontext->heap_limit < re->limit_heap)?
+ mcontext->heap_limit : re->limit_heap;
+
+mb->match_limit = (mcontext->match_limit < re->limit_match)?
+ mcontext->match_limit : re->limit_match;
+
+mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)?
+ mcontext->depth_limit : re->limit_depth;
+
+/* If a pattern has very many capturing parentheses, the frame size may be very
+large. Ensure that there are at least 10 available frames by getting an initial
+vector on the heap if necessary, except when the heap limit prevents this. Get
+fewer if possible. (The heap limit is in kilobytes.) */
+
+if (frame_size <= START_FRAMES_SIZE/10)
{
- ocount = re->top_backref * 3 + 3;
- mb->ovector = (PCRE2_SIZE *)(mb->memctl.malloc(ocount * sizeof(PCRE2_SIZE),
- mb->memctl.memory_data));
- if (mb->ovector == NULL) return PCRE2_ERROR_NOMEMORY;
- using_temporary_offsets = TRUE;
+ mb->match_frames = mb->stack_frames; /* Initial frame vector on the stack */
+ mb->frame_vector_size = ((START_FRAMES_SIZE/frame_size) * frame_size);
}
else
{
- ocount = 3 * match_data->oveccount;
- mb->ovector = match_data->ovector;
+ mb->frame_vector_size = frame_size * 10;
+ if ((mb->frame_vector_size / 1024) > mb->heap_limit)
+ {
+ if (frame_size > mb->heap_limit * 1024) return PCRE2_ERROR_HEAPLIMIT;
+ mb->frame_vector_size = ((mb->heap_limit * 1024)/frame_size) * frame_size;
+ }
+ mb->match_frames = mb->memctl.malloc(mb->frame_vector_size,
+ mb->memctl.memory_data);
+ if (mb->match_frames == NULL) return PCRE2_ERROR_NOMEMORY;
}
-mb->offset_end = ocount;
-mb->offset_max = (2*ocount)/3;
+mb->match_frames_top =
+ (heapframe *)((char *)mb->match_frames + mb->frame_vector_size);
-/* Reset the working variable associated with each extraction. These should
-never be used unless previously set, but they get saved and restored, and so we
-initialize them to avoid reading uninitialized locations. Also, unset the
-offsets for the matched string. This is really just for tidiness with callouts,
-in case they inspect these fields. */
+/* Write to the ovector within the first frame to mark every capture unset and
+to avoid uninitialized memory read errors when it is copied to a new frame. */
-if (ocount > 0)
- {
- PCRE2_SIZE *iptr = mb->ovector + ocount;
- PCRE2_SIZE *iend = iptr - re->top_bracket;
- if (iend < mb->ovector + 2) iend = mb->ovector + 2;
- while (--iptr >= iend) *iptr = PCRE2_UNSET;
- mb->ovector[0] = mb->ovector[1] = PCRE2_UNSET;
- }
+memset((char *)(mb->match_frames) + offsetof(heapframe, ovector), 0xff,
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE));
-/* Set up the first code unit to match, if available. The first_codeunit value
-is never set for an anchored regular expression, but the anchoring may be
-forced at run time, so we have to test for anchoring. The first code unit may
-be unset for an unanchored pattern, of course. If there's no first code unit
-there may be a bitmap of possible first characters. */
+/* Pointers to the individual character tables */
+
+mb->lcc = re->tables + lcc_offset;
+mb->fcc = re->tables + fcc_offset;
+mb->ctypes = re->tables + ctypes_offset;
+
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
-if (!anchored)
+if ((re->flags & PCRE2_FIRSTSET) != 0)
{
- if ((re->flags & PCRE2_FIRSTSET) != 0)
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
{
- has_first_cu = TRUE;
- first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
- if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
- {
- first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu);
+ first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu);
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8
- if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu);
+ if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu);
#endif
- }
}
- else
- if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
- start_bits = re->start_bitmap;
}
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
-/* For anchored or unanchored matches, there may be a "last known required
-character" set. */
+/* There may also be a "last known required character" set. */
if ((re->flags & PCRE2_LASTSET) != 0)
{
@@ -6783,7 +6353,6 @@ the loop runs just once. */
for(;;)
{
PCRE2_SPTR new_start_match;
- mb->capture_last = 0;
/* ----------------- Start of match optimizations ---------------- */
@@ -6794,13 +6363,11 @@ for(;;)
if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
{
- PCRE2_SPTR save_end_subject = end_subject;
-
/* If firstline is TRUE, the start of the match is constrained to the first
line of a multiline string. That is, the match must be before or at the
- first newline. Implement this by temporarily adjusting end_subject so that
- we stop the optimization scans at a newline. If the match fails at the
- newline, later code breaks this loop. */
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the scans for a first code unit at a newline.
+ If the match fails at the newline, later code breaks the loop. */
if (firstline)
{
@@ -6808,102 +6375,179 @@ for(;;)
#ifdef SUPPORT_UNICODE
if (utf)
{
- while (t < mb->end_subject && !IS_NEWLINE(t))
+ while (t < end_subject && !IS_NEWLINE(t))
{
t++;
- ACROSSCHAR(t < end_subject, *t, t++);
+ ACROSSCHAR(t < end_subject, t, t++);
}
}
else
#endif
- while (t < mb->end_subject && !IS_NEWLINE(t)) t++;
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
end_subject = t;
}
- /* Advance to a unique first code unit if there is one. In 8-bit mode, the
- use of memchr() gives a big speed up. */
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
- if (has_first_cu)
+ if (anchored)
{
- PCRE2_UCHAR smc;
- if (first_cu != first_cu2)
- while (start_match < end_subject &&
- (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2)
- start_match++;
- else
+ if (has_first_cu || start_bits != NULL)
{
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
#if PCRE2_CODE_UNIT_WIDTH != 8
- while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu)
- start_match++;
-#else
- start_match = memchr(start_match, first_cu, end_subject - start_match);
- if (start_match == NULL) start_match = end_subject;
+ if (c > 255) c = 255;
#endif
+ ok = (start_bits[c/8] & (1 << (c&7))) != 0;
+ }
+ }
+ if (!ok)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
}
}
- /* Or to just after a linebreak for a multiline match */
+ /* Not anchored. Advance to a unique first code unit if there is one. In
+ 8-bit mode, the use of memchr() gives a big speed up, even though we have
+ to call it twice in caseless mode, in order to find the earliest occurrence
+ of the character in either of its cases. */
- else if (startline)
+ else
{
- if (start_match > mb->start_subject + start_offset)
+ if (has_first_cu)
{
-#ifdef SUPPORT_UNICODE
- if (utf)
+ if (first_cu != first_cu2) /* Caseless */
{
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
start_match++;
- ACROSSCHAR(start_match < end_subject, *start_match,
- start_match++);
- }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp1 =
+ memchr(start_match, first_cu, end_subject-start_match);
+ PCRE2_SPTR pp2 =
+ memchr(start_match, first_cu2, end_subject-start_match);
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+#endif
}
+
+ /* The caseful case */
+
else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
#endif
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- start_match++;
+ }
- /* If we have just passed a CR and the newline option is ANY or
- ANYCRLF, and we are now at a LF, advance the match position by one more
- code unit. */
+ /* If we can't find the required code unit, having reached the true end
+ of the subject, break the bumpalong loop, to force a match failure,
+ except when doing partial matching, when we let the next cycle run at
+ the end of the subject. To see why, consider the pattern /(?<=abc)def/,
+ which partially matches "abc", even though the string does not contain
+ the starting character "d". If we have not reached the true end of the
+ subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified)
+ we also let the cycle run, because the matching string is legitimately
+ allowed to start with the first code unit of a newline. */
+
+ if (!mb->partial && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
- if (start_match[-1] == CHAR_CR &&
- (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
- start_match < end_subject &&
- UCHAR21TEST(start_match) == CHAR_NL)
- start_match++;
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
+
+ else if (startline)
+ {
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
}
- }
- /* Or to a non-unique first code unit if any have been identified. The
- bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all
- code units greater than 254 set the 255 bit. */
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
- else if (start_bits != NULL)
- {
- while (start_match < end_subject)
+ else if (start_bits != NULL)
{
- uint32_t c = UCHAR21TEST(start_match);
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
#if PCRE2_CODE_UNIT_WIDTH != 8
- if (c > 255) c = 255;
+ if (c > 255) c = 255;
#endif
- if ((start_bits[c/8] & (1 << (c&7))) != 0) break;
- start_match++;
+ if ((start_bits[c/8] & (1 << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next few lines. */
+
+ if (!mb->partial && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
}
- }
+ } /* End first code unit handling */
/* Restore fudged end_subject */
- end_subject = save_end_subject;
+ end_subject = mb->end_subject;
- /* The following two optimizations are disabled for partial matching. */
+ /* The following two optimizations must be disabled for partial matching. */
if (!mb->partial)
{
- /* The minimum matching length is a lower bound; no actual string of that
- length may actually match the pattern. Although the value is, strictly,
- in characters, we treat it as code units to avoid spending too much time
- in this optimization. */
+ /* The minimum matching length is a lower bound; no string of that length
+ may actually match the pattern. Although the value is, strictly, in
+ characters, we treat it as code units to avoid spending too much time in
+ this optimization. */
if (end_subject - start_match < re->minlength)
{
@@ -6912,12 +6556,16 @@ for(;;)
}
/* If req_cu is set, we know that that code unit must appear in the
- subject for the match to succeed. If the first code unit is set, req_cu
- must be later in the subject; otherwise the test starts at the match
- point. This optimization can save a huge amount of backtracking in
- patterns with nested unlimited repeats that aren't going to match.
- Writing separate code for cased/caseless versions makes it go faster, as
- does using an autoincrement and backing off on a match.
+ subject for the (non-partial) match to succeed. If the first code unit is
+ set, req_cu must be later in the subject; otherwise the test starts at
+ the match point. This optimization can save a huge amount of backtracking
+ in patterns with nested unlimited repeats that aren't going to match.
+ Writing separate code for caseful/caseless versions makes it go faster,
+ as does using an autoincrement and backing off on a match. As in the case
+ of the first code unit, using memchr() in the 8-bit library gives a big
+ speed up. Unlike the first_cu check above, we do not need to call
+ memchr() twice in the caseless case because we only need to check for the
+ presence of the character in either case, not find the first occurrence.
HOWEVER: when the subject string is very, very long, searching to its end
can take a long time, and give bad performance on quite ordinary
@@ -6930,27 +6578,52 @@ for(;;)
PCRE2_SPTR p = start_match + (has_first_cu? 1:0);
/* We don't need to repeat the search if we haven't yet reached the
- place we found it at last time. */
+ place we found it last time round the bumpalong loop. */
if (p > req_cu_ptr)
{
- if (req_cu != req_cu2)
+ if (p < end_subject)
{
- while (p < end_subject)
+ if (req_cu != req_cu2) /* Caseless */
{
- uint32_t pp = UCHAR21INCTEST(p);
- if (pp == req_cu || pp == req_cu2) { p--; break; }
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ do
+ {
+ uint32_t pp = UCHAR21INCTEST(p);
+ if (pp == req_cu || pp == req_cu2) { p--; break; }
+ }
+ while (p < end_subject);
+
+#else /* 8-bit code units */
+ PCRE2_SPTR pp = p;
+ p = memchr(pp, req_cu, end_subject - pp);
+ if (p == NULL)
+ {
+ p = memchr(pp, req_cu2, end_subject - pp);
+ if (p == NULL) p = end_subject;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
}
- }
- else
- {
- while (p < end_subject)
+
+ /* The caseful case */
+
+ else
{
- if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ do
+ {
+ if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+ }
+ while (p < end_subject);
+
+#else /* 8-bit code units */
+ p = memchr(p, req_cu, end_subject - p);
+ if (p == NULL) p = end_subject;
+#endif
}
}
- /* If we can't find the required code unit, break the matching loop,
+ /* If we can't find the required code unit, break the bumpalong loop,
forcing a match failure. */
if (p >= end_subject)
@@ -6960,8 +6633,8 @@ for(;;)
}
/* If we have found the required code unit, save the point where we
- found it, so that we don't search again next time round the loop if
- the start hasn't passed this code unit yet. */
+ found it, so that we don't search again next time round the bumpalong
+ loop if the start hasn't yet passed this code unit. */
req_cu_ptr = p;
}
@@ -6982,14 +6655,17 @@ for(;;)
/* OK, we can now run the match. If "hitend" is set afterwards, remember the
first starting point for which a partial match was found. */
- mb->start_match_ptr = start_match;
+ cb.start_match = (PCRE2_SIZE)(start_match - subject);
+ cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH;
+
mb->start_used_ptr = start_match;
mb->last_used_ptr = start_match;
mb->match_call_count = 0;
- mb->match_function_type = 0;
mb->end_offset_top = 0;
mb->skip_arg_count = 0;
- rc = match(start_match, mb->start_code, start_match, 2, mb, NULL, 0);
+
+ rc = match(start_match, mb->start_code, match_data->ovector,
+ match_data->oveccount, re->top_bracket, frame_size, mb);
if (mb->hitend && start_partial == NULL)
{
@@ -7015,9 +6691,9 @@ for(;;)
greater than the match we have just done, treat it as NOMATCH. */
case MATCH_SKIP:
- if (mb->start_match_ptr > start_match)
+ if (mb->verb_skip_ptr > start_match)
{
- new_start_match = mb->start_match_ptr;
+ new_start_match = mb->verb_skip_ptr;
break;
}
/* Fall through */
@@ -7032,7 +6708,7 @@ for(;;)
new_start_match = start_match + 1;
#ifdef SUPPORT_UNICODE
if (utf)
- ACROSSCHAR(new_start_match < end_subject, *new_start_match,
+ ACROSSCHAR(new_start_match < end_subject, new_start_match,
new_start_match++);
#endif
break;
@@ -7091,11 +6767,11 @@ for(;;)
/* ==========================================================================*/
-/* When we reach here, one of the stopping conditions is true:
+/* When we reach here, one of the following stopping conditions is true:
(1) The match succeeded, either completely, or partially;
-(2) The pattern is anchored or the match was failed by (*COMMIT);
+(2) The pattern is anchored or the match was failed after (*COMMIT);
(3) We are past the end of the subject or the bumpalong limit;
@@ -7109,18 +6785,10 @@ for(;;)
ENDLOOP:
-#ifdef HEAP_MATCH_RECURSE
-release_match_heapframes(&frame_zero, mb);
-#endif
-
-/* Release any frames that were saved from recursions. */
+/* Release an enlarged frame vector that is on the heap. */
-while (mb->ovecsave_chain != NULL)
- {
- ovecsave_frame *this = mb->ovecsave_chain;
- mb->ovecsave_chain = this->next;
- mb->memctl.free(this, mb->memctl.memory_data);
- }
+if (mb->match_frames != mb->stack_frames)
+ mb->memctl.free(mb->match_frames, mb->memctl.memory_data);
/* Fill in fields that are always returned in the match data. */
@@ -7129,68 +6797,14 @@ match_data->subject = subject;
match_data->mark = mb->mark;
match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER;
-/* Handle a fully successful match. */
+/* Handle a fully successful match. Set the return code to the number of
+captured strings, or 0 if there were too many to fit into the ovector, and then
+set the remaining returned values before returning. */
-if (rc == MATCH_MATCH || rc == MATCH_ACCEPT)
+if (rc == MATCH_MATCH)
{
- uint32_t arg_offset_max = 2 * match_data->oveccount;
-
- /* When the offset vector is big enough to deal with any backreferences,
- captured substring offsets will already be set up. In the case where we had
- to get some local memory to hold offsets for backreference processing, copy
- those that we can. In this case there need not be overflow if certain parts
- of the pattern were not used, even though there are more capturing
- parentheses than vector slots. */
-
- if (using_temporary_offsets)
- {
- if (arg_offset_max >= 4)
- {
- memcpy(match_data->ovector + 2, mb->ovector + 2,
- (arg_offset_max - 2) * sizeof(PCRE2_SIZE));
- }
- if (mb->end_offset_top > arg_offset_max) mb->capture_last |= OVFLBIT;
- mb->memctl.free(mb->ovector, mb->memctl.memory_data);
- }
-
- /* Set the return code to the number of captured strings, or 0 if there were
- too many to fit into the ovector. */
-
- match_data->rc = ((mb->capture_last & OVFLBIT) != 0)?
- 0 : (int)mb->end_offset_top/2;
-
- /* If there is space in the offset vector, set any pairs that follow the
- highest-numbered captured string but are less than the number of capturing
- groups in the pattern (and are within the ovector) to PCRE2_UNSET. It is
- documented that this happens. In earlier versions, the whole set of potential
- capturing offsets was initialized each time round the loop, but this is
- handled differently now. "Gaps" are set to PCRE2_UNSET dynamically instead
- (this fixed a bug). Thus, it is only those at the end that need setting here.
- We can't just mark them all unset at the start of the whole thing because
- they may get set in one branch that is not the final matching branch. */
-
- if (mb->end_offset_top/2 <= re->top_bracket)
- {
- PCRE2_SIZE *iptr, *iend;
- int resetcount = re->top_bracket + 1;
- if (resetcount > match_data->oveccount) resetcount = match_data->oveccount;
- iptr = match_data->ovector + mb->end_offset_top;
- iend = match_data->ovector + 2 * resetcount;
- while (iptr < iend) *iptr++ = PCRE2_UNSET;
- }
-
- /* If there is space, set up the whole thing as substring 0. The value of
- mb->start_match_ptr might be modified if \K was encountered on the success
- matching path. */
-
- if (match_data->oveccount < 1) rc = 0; else
- {
- match_data->ovector[0] = mb->start_match_ptr - mb->start_subject;
- match_data->ovector[1] = mb->end_match_ptr - mb->start_subject;
- }
-
- /* Set the remaining returned values */
-
+ match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)?
+ 0 : (int)mb->end_offset_top/2 + 1;
match_data->startchar = start_match - subject;
match_data->leftchar = mb->start_used_ptr - subject;
match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)?
@@ -7206,18 +6820,14 @@ match_data->mark = mb->nomatch_mark;
/* For anything other than nomatch or partial match, just return the code. */
-if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL)
- match_data->rc = rc;
+if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc;
-/* Else handle a partial match. */
+/* Handle a partial match. */
else if (match_partial != NULL)
{
- if (match_data->oveccount > 0)
- {
- match_data->ovector[0] = match_partial - subject;
- match_data->ovector[1] = end_subject - subject;
- }
+ match_data->ovector[0] = match_partial - subject;
+ match_data->ovector[1] = end_subject - subject;
match_data->startchar = match_partial - subject;
match_data->leftchar = start_partial - subject;
match_data->rightchar = end_subject - subject;
@@ -7228,10 +6838,6 @@ else if (match_partial != NULL)
else match_data->rc = PCRE2_ERROR_NOMATCH;
-/* Free any temporary offsets. */
-
-if (using_temporary_offsets)
- mb->memctl.free(mb->ovector, mb->memctl.memory_data);
return match_data->rc;
}
diff --git a/thirdparty/pcre2/src/pcre2_match_data.c b/thirdparty/pcre2/src/pcre2_match_data.c
index 85ac998348..b297f326b5 100644
--- a/thirdparty/pcre2/src/pcre2_match_data.c
+++ b/thirdparty/pcre2/src/pcre2_match_data.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,7 @@ POSSIBILITY OF SUCH DAMAGE.
* Create a match data block given ovector size *
*************************************************/
-/* A minimum of 1 is imposed on the number of ovector triplets. */
+/* A minimum of 1 is imposed on the number of ovector pairs. */
PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION
pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext)
@@ -59,7 +59,7 @@ pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext)
pcre2_match_data *yield;
if (oveccount < 1) oveccount = 1;
yield = PRIV(memctl_malloc)(
- sizeof(pcre2_match_data) + 3*oveccount*sizeof(PCRE2_SIZE),
+ offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE),
(pcre2_memctl *)gcontext);
if (yield == NULL) return NULL;
yield->oveccount = oveccount;
diff --git a/thirdparty/pcre2/src/pcre2_pattern_info.c b/thirdparty/pcre2/src/pcre2_pattern_info.c
index 5b32a905b0..906e9198f5 100644
--- a/thirdparty/pcre2/src/pcre2_pattern_info.c
+++ b/thirdparty/pcre2/src/pcre2_pattern_info.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -75,10 +75,13 @@ if (where == NULL) /* Requests field length */
case PCRE2_INFO_BACKREFMAX:
case PCRE2_INFO_BSR:
case PCRE2_INFO_CAPTURECOUNT:
+ case PCRE2_INFO_DEPTHLIMIT:
+ case PCRE2_INFO_EXTRAOPTIONS:
case PCRE2_INFO_FIRSTCODETYPE:
case PCRE2_INFO_FIRSTCODEUNIT:
case PCRE2_INFO_HASBACKSLASHC:
case PCRE2_INFO_HASCRORLF:
+ case PCRE2_INFO_HEAPLIMIT:
case PCRE2_INFO_JCHANGED:
case PCRE2_INFO_LASTCODETYPE:
case PCRE2_INFO_LASTCODEUNIT:
@@ -89,7 +92,6 @@ if (where == NULL) /* Requests field length */
case PCRE2_INFO_NAMEENTRYSIZE:
case PCRE2_INFO_NAMECOUNT:
case PCRE2_INFO_NEWLINE:
- case PCRE2_INFO_RECURSIONLIMIT:
return sizeof(uint32_t);
case PCRE2_INFO_FIRSTBITMAP:
@@ -97,6 +99,7 @@ if (where == NULL) /* Requests field length */
case PCRE2_INFO_JITSIZE:
case PCRE2_INFO_SIZE:
+ case PCRE2_INFO_FRAMESIZE:
return sizeof(size_t);
case PCRE2_INFO_NAMETABLE:
@@ -137,6 +140,15 @@ switch(what)
*((uint32_t *)where) = re->top_bracket;
break;
+ case PCRE2_INFO_DEPTHLIMIT:
+ *((uint32_t *)where) = re->limit_depth;
+ if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_EXTRAOPTIONS:
+ *((uint32_t *)where) = re->extra_options;
+ break;
+
case PCRE2_INFO_FIRSTCODETYPE:
*((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 :
((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0;
@@ -152,6 +164,11 @@ switch(what)
&(re->start_bitmap[0]) : NULL;
break;
+ case PCRE2_INFO_FRAMESIZE:
+ *((size_t *)where) = offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE);
+ break;
+
case PCRE2_INFO_HASBACKSLASHC:
*((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0;
break;
@@ -160,6 +177,11 @@ switch(what)
*((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0;
break;
+ case PCRE2_INFO_HEAPLIMIT:
+ *((uint32_t *)where) = re->limit_heap;
+ if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
case PCRE2_INFO_JCHANGED:
*((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0;
break;
@@ -215,11 +237,6 @@ switch(what)
*((uint32_t *)where) = re->newline_convention;
break;
- case PCRE2_INFO_RECURSIONLIMIT:
- *((uint32_t *)where) = re->limit_recursion;
- if (re->limit_recursion == UINT32_MAX) return PCRE2_ERROR_UNSET;
- break;
-
case PCRE2_INFO_SIZE:
*((size_t *)where) = re->blocksize;
break;
@@ -255,11 +272,15 @@ pcre2_real_code *re = (pcre2_real_code *)code;
pcre2_callout_enumerate_block cb;
PCRE2_SPTR cc;
#ifdef SUPPORT_UNICODE
-BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
+BOOL utf;
#endif
if (re == NULL) return PCRE2_ERROR_NULL;
+#ifdef SUPPORT_UNICODE
+utf = (re->overall_options & PCRE2_UTF) != 0;
+#endif
+
/* Check that the first field in the block is the magic number. If it is not,
return with PCRE2_ERROR_BADMAGIC. */
diff --git a/thirdparty/pcre2/src/pcre2_printint.c b/thirdparty/pcre2/src/pcre2_printint.c
deleted file mode 100644
index 6207497648..0000000000
--- a/thirdparty/pcre2/src/pcre2_printint.c
+++ /dev/null
@@ -1,832 +0,0 @@
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains a PCRE private debugging function for printing out the
-internal form of a compiled regular expression, along with some supporting
-local functions. This source file is #included in pcre2test.c at each supported
-code unit width, with PCRE2_SUFFIX set appropriately, just like the functions
-that comprise the library. It can also optionally be included in
-pcre2_compile.c for detailed debugging in error situations. */
-
-
-/* Tables of operator names. The same 8-bit table is used for all code unit
-widths, so it must be defined only once. The list itself is defined in
-pcre2_internal.h, which is #included by pcre2test before this file. */
-
-#ifndef OP_LISTS_DEFINED
-static const char *OP_names[] = { OP_NAME_LIST };
-#define OP_LISTS_DEFINED
-#endif
-
-/* The functions and tables herein must all have mode-dependent names. */
-
-#define OP_lengths PCRE2_SUFFIX(OP_lengths_)
-#define get_ucpname PCRE2_SUFFIX(get_ucpname_)
-#define pcre2_printint PCRE2_SUFFIX(pcre2_printint_)
-#define print_char PCRE2_SUFFIX(print_char_)
-#define print_custring PCRE2_SUFFIX(print_custring_)
-#define print_custring_bylen PCRE2_SUFFIX(print_custring_bylen_)
-#define print_prop PCRE2_SUFFIX(print_prop_)
-
-/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
-the definition is next to the definition of the opcodes in pcre2_internal.h.
-The contents of the table are, however, mode-dependent. */
-
-static const uint8_t OP_lengths[] = { OP_LENGTHS };
-
-
-
-/*************************************************
-* Print one character from a string *
-*************************************************/
-
-/* In UTF mode the character may occupy more than one code unit.
-
-Arguments:
- f file to write to
- ptr pointer to first code unit of the character
- utf TRUE if string is UTF (will be FALSE if UTF is not supported)
-
-Returns: number of additional code units used
-*/
-
-static unsigned int
-print_char(FILE *f, PCRE2_SPTR ptr, BOOL utf)
-{
-uint32_t c = *ptr;
-BOOL one_code_unit = !utf;
-
-/* If UTF is supported and requested, check for a valid single code unit. */
-
-#ifdef SUPPORT_UNICODE
-if (utf)
- {
-#if PCRE2_CODE_UNIT_WIDTH == 8
- one_code_unit = c < 0x80;
-#elif PCRE2_CODE_UNIT_WIDTH == 16
- one_code_unit = (c & 0xfc00) != 0xd800;
-#else
- one_code_unit = (c & 0xfffff800u) != 0xd800u;
-#endif /* CODE_UNIT_WIDTH */
- }
-#endif /* SUPPORT_UNICODE */
-
-/* Handle a valid one-code-unit character at any width. */
-
-if (one_code_unit)
- {
- if (PRINTABLE(c)) fprintf(f, "%c", (char)c);
- else if (c < 0x80) fprintf(f, "\\x%02x", c);
- else fprintf(f, "\\x{%02x}", c);
- return 0;
- }
-
-/* Code for invalid UTF code units and multi-unit UTF characters is different
-for each width. If UTF is not supported, control should never get here, but we
-need a return statement to keep the compiler happy. */
-
-#ifndef SUPPORT_UNICODE
-return 0;
-#else
-
-/* Malformed UTF-8 should occur only if the sanity check has been turned off.
-Rather than swallow random bytes, just stop if we hit a bad one. Print it with
-\X instead of \x as an indication. */
-
-#if PCRE2_CODE_UNIT_WIDTH == 8
-if ((c & 0xc0) != 0xc0)
- {
- fprintf(f, "\\X{%x}", c); /* Invalid starting byte */
- return 0;
- }
-else
- {
- int i;
- int a = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes */
- int s = 6*a;
- c = (c & PRIV(utf8_table3)[a]) << s;
- for (i = 1; i <= a; i++)
- {
- if ((ptr[i] & 0xc0) != 0x80)
- {
- fprintf(f, "\\X{%x}", c); /* Invalid secondary byte */
- return i - 1;
- }
- s -= 6;
- c |= (ptr[i] & 0x3f) << s;
- }
- fprintf(f, "\\x{%x}", c);
- return a;
-}
-#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
-
-/* UTF-16: rather than swallow a low surrogate, just stop if we hit a bad one.
-Print it with \X instead of \x as an indication. */
-
-#if PCRE2_CODE_UNIT_WIDTH == 16
-if ((ptr[1] & 0xfc00) != 0xdc00)
- {
- fprintf(f, "\\X{%x}", c);
- return 0;
- }
-c = (((c & 0x3ff) << 10) | (ptr[1] & 0x3ff)) + 0x10000;
-fprintf(f, "\\x{%x}", c);
-return 1;
-#endif /* PCRE2_CODE_UNIT_WIDTH == 16 */
-
-/* For UTF-32 we get here only for a malformed code unit, which should only
-occur if the sanity check has been turned off. Print it with \X instead of \x
-as an indication. */
-
-#if PCRE2_CODE_UNIT_WIDTH == 32
-fprintf(f, "\\X{%x}", c);
-return 0;
-#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
-#endif /* SUPPORT_UNICODE */
-}
-
-
-
-/*************************************************
-* Print string as a list of code units *
-*************************************************/
-
-/* These take no account of UTF as they always print each individual code unit.
-The string is zero-terminated for print_custring(); the length is given for
-print_custring_bylen().
-
-Arguments:
- f file to write to
- ptr point to the string
- len length for print_custring_bylen()
-
-Returns: nothing
-*/
-
-static void
-print_custring(FILE *f, PCRE2_SPTR ptr)
-{
-while (*ptr != '\0')
- {
- uint32_t c = *ptr++;
- if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c);
- }
-}
-
-static void
-print_custring_bylen(FILE *f, PCRE2_SPTR ptr, PCRE2_UCHAR len)
-{
-for (; len > 0; len--)
- {
- uint32_t c = *ptr++;
- if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c);
- }
-}
-
-
-
-/*************************************************
-* Find Unicode property name *
-*************************************************/
-
-/* When there is no UTF/UCP support, the table of names does not exist. This
-function should not be called in such configurations, because a pattern that
-tries to use Unicode properties won't compile. Rather than put lots of #ifdefs
-into the main code, however, we just put one into this function. */
-
-static const char *
-get_ucpname(unsigned int ptype, unsigned int pvalue)
-{
-#ifdef SUPPORT_UNICODE
-int i;
-for (i = PRIV(utt_size) - 1; i >= 0; i--)
- {
- if (ptype == PRIV(utt)[i].type && pvalue == PRIV(utt)[i].value) break;
- }
-return (i >= 0)? PRIV(utt_names) + PRIV(utt)[i].name_offset : "??";
-#else /* No UTF support */
-(void)ptype;
-(void)pvalue;
-return "??";
-#endif /* SUPPORT_UNICODE */
-}
-
-
-
-/*************************************************
-* Print Unicode property value *
-*************************************************/
-
-/* "Normal" properties can be printed from tables. The PT_CLIST property is a
-pseudo-property that contains a pointer to a list of case-equivalent
-characters.
-
-Arguments:
- f file to write to
- code pointer in the compiled code
- before text to print before
- after text to print after
-
-Returns: nothing
-*/
-
-static void
-print_prop(FILE *f, PCRE2_SPTR code, const char *before, const char *after)
-{
-if (code[1] != PT_CLIST)
- {
- fprintf(f, "%s%s %s%s", before, OP_names[*code], get_ucpname(code[1],
- code[2]), after);
- }
-else
- {
- const char *not = (*code == OP_PROP)? "" : "not ";
- const uint32_t *p = PRIV(ucd_caseless_sets) + code[2];
- fprintf (f, "%s%sclist", before, not);
- while (*p < NOTACHAR) fprintf(f, " %04x", *p++);
- fprintf(f, "%s", after);
- }
-}
-
-
-
-/*************************************************
-* Print compiled pattern *
-*************************************************/
-
-/* The print_lengths flag controls whether offsets and lengths of items are
-printed. Lenths can be turned off from pcre2test so that automatic tests on
-bytecode can be written that do not depend on the value of LINK_SIZE.
-
-Arguments:
- re a compiled pattern
- f the file to write to
- print_lengths show various lengths
-
-Returns: nothing
-*/
-
-static void
-pcre2_printint(pcre2_code *re, FILE *f, BOOL print_lengths)
-{
-PCRE2_SPTR codestart, nametable, code;
-uint32_t nesize = re->name_entry_size;
-BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
-
-nametable = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code));
-code = codestart = nametable + re->name_count * re->name_entry_size;
-
-for(;;)
- {
- PCRE2_SPTR ccode;
- uint32_t c;
- int i;
- const char *flag = " ";
- unsigned int extra = 0;
-
- if (print_lengths)
- fprintf(f, "%3d ", (int)(code - codestart));
- else
- fprintf(f, " ");
-
- switch(*code)
- {
-/* ========================================================================== */
- /* These cases are never obeyed. This is a fudge that causes a compile-
- time error if the vectors OP_names or OP_lengths, which are indexed
- by opcode, are not the correct length. It seems to be the only way to do
- such a check at compile time, as the sizeof() operator does not work in
- the C preprocessor. */
-
- case OP_TABLE_LENGTH:
- case OP_TABLE_LENGTH +
- ((sizeof(OP_names)/sizeof(const char *) == OP_TABLE_LENGTH) &&
- (sizeof(OP_lengths) == OP_TABLE_LENGTH)):
- break;
-/* ========================================================================== */
-
- case OP_END:
- fprintf(f, " %s\n", OP_names[*code]);
- fprintf(f, "------------------------------------------------------------------\n");
- return;
-
- case OP_CHAR:
- fprintf(f, " ");
- do
- {
- code++;
- code += 1 + print_char(f, code, utf);
- }
- while (*code == OP_CHAR);
- fprintf(f, "\n");
- continue;
-
- case OP_CHARI:
- fprintf(f, " /i ");
- do
- {
- code++;
- code += 1 + print_char(f, code, utf);
- }
- while (*code == OP_CHARI);
- fprintf(f, "\n");
- continue;
-
- case OP_CBRA:
- case OP_CBRAPOS:
- case OP_SCBRA:
- case OP_SCBRAPOS:
- if (print_lengths) fprintf(f, "%3d ", GET(code, 1));
- else fprintf(f, " ");
- fprintf(f, "%s %d", OP_names[*code], GET2(code, 1+LINK_SIZE));
- break;
-
- case OP_BRA:
- case OP_BRAPOS:
- case OP_SBRA:
- case OP_SBRAPOS:
- case OP_KETRMAX:
- case OP_KETRMIN:
- case OP_KETRPOS:
- case OP_ALT:
- case OP_KET:
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- case OP_ONCE:
- case OP_ONCE_NC:
- case OP_COND:
- case OP_SCOND:
- case OP_REVERSE:
- if (print_lengths) fprintf(f, "%3d ", GET(code, 1));
- else fprintf(f, " ");
- fprintf(f, "%s", OP_names[*code]);
- break;
-
- case OP_CLOSE:
- fprintf(f, " %s %d", OP_names[*code], GET2(code, 1));
- break;
-
- case OP_CREF:
- fprintf(f, "%3d %s", GET2(code,1), OP_names[*code]);
- break;
-
- case OP_DNCREF:
- {
- PCRE2_SPTR entry = nametable + (GET2(code, 1) * nesize) + IMM2_SIZE;
- fprintf(f, " %s Cond ref <", flag);
- print_custring(f, entry);
- fprintf(f, ">%d", GET2(code, 1 + IMM2_SIZE));
- }
- break;
-
- case OP_RREF:
- c = GET2(code, 1);
- if (c == RREF_ANY)
- fprintf(f, " Cond recurse any");
- else
- fprintf(f, " Cond recurse %d", c);
- break;
-
- case OP_DNRREF:
- {
- PCRE2_SPTR entry = nametable + (GET2(code, 1) * nesize) + IMM2_SIZE;
- fprintf(f, " %s Cond recurse <", flag);
- print_custring(f, entry);
- fprintf(f, ">%d", GET2(code, 1 + IMM2_SIZE));
- }
- break;
-
- case OP_FALSE:
- fprintf(f, " Cond false");
- break;
-
- case OP_TRUE:
- fprintf(f, " Cond true");
- break;
-
- case OP_STARI:
- case OP_MINSTARI:
- case OP_POSSTARI:
- case OP_PLUSI:
- case OP_MINPLUSI:
- case OP_POSPLUSI:
- case OP_QUERYI:
- case OP_MINQUERYI:
- case OP_POSQUERYI:
- flag = "/i";
- /* Fall through */
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPOSSTAR:
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEPOSPLUS:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSQUERY:
- fprintf(f, " %s ", flag);
-
- if (*code >= OP_TYPESTAR)
- {
- if (code[1] == OP_PROP || code[1] == OP_NOTPROP)
- {
- print_prop(f, code + 1, "", " ");
- extra = 2;
- }
- else fprintf(f, "%s", OP_names[code[1]]);
- }
- else extra = print_char(f, code+1, utf);
- fprintf(f, "%s", OP_names[*code]);
- break;
-
- case OP_EXACTI:
- case OP_UPTOI:
- case OP_MINUPTOI:
- case OP_POSUPTOI:
- flag = "/i";
- /* Fall through */
- case OP_EXACT:
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- fprintf(f, " %s ", flag);
- extra = print_char(f, code + 1 + IMM2_SIZE, utf);
- fprintf(f, "{");
- if (*code != OP_EXACT && *code != OP_EXACTI) fprintf(f, "0,");
- fprintf(f, "%d}", GET2(code,1));
- if (*code == OP_MINUPTO || *code == OP_MINUPTOI) fprintf(f, "?");
- else if (*code == OP_POSUPTO || *code == OP_POSUPTOI) fprintf(f, "+");
- break;
-
- case OP_TYPEEXACT:
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEPOSUPTO:
- if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
- {
- print_prop(f, code + IMM2_SIZE + 1, " ", " ");
- extra = 2;
- }
- else fprintf(f, " %s", OP_names[code[1 + IMM2_SIZE]]);
- fprintf(f, "{");
- if (*code != OP_TYPEEXACT) fprintf(f, "0,");
- fprintf(f, "%d}", GET2(code,1));
- if (*code == OP_TYPEMINUPTO) fprintf(f, "?");
- else if (*code == OP_TYPEPOSUPTO) fprintf(f, "+");
- break;
-
- case OP_NOTI:
- flag = "/i";
- /* Fall through */
- case OP_NOT:
- fprintf(f, " %s [^", flag);
- extra = print_char(f, code + 1, utf);
- fprintf(f, "]");
- break;
-
- case OP_NOTSTARI:
- case OP_NOTMINSTARI:
- case OP_NOTPOSSTARI:
- case OP_NOTPLUSI:
- case OP_NOTMINPLUSI:
- case OP_NOTPOSPLUSI:
- case OP_NOTQUERYI:
- case OP_NOTMINQUERYI:
- case OP_NOTPOSQUERYI:
- flag = "/i";
- /* Fall through */
-
- case OP_NOTSTAR:
- case OP_NOTMINSTAR:
- case OP_NOTPOSSTAR:
- case OP_NOTPLUS:
- case OP_NOTMINPLUS:
- case OP_NOTPOSPLUS:
- case OP_NOTQUERY:
- case OP_NOTMINQUERY:
- case OP_NOTPOSQUERY:
- fprintf(f, " %s [^", flag);
- extra = print_char(f, code + 1, utf);
- fprintf(f, "]%s", OP_names[*code]);
- break;
-
- case OP_NOTEXACTI:
- case OP_NOTUPTOI:
- case OP_NOTMINUPTOI:
- case OP_NOTPOSUPTOI:
- flag = "/i";
- /* Fall through */
-
- case OP_NOTEXACT:
- case OP_NOTUPTO:
- case OP_NOTMINUPTO:
- case OP_NOTPOSUPTO:
- fprintf(f, " %s [^", flag);
- extra = print_char(f, code + 1 + IMM2_SIZE, utf);
- fprintf(f, "]{");
- if (*code != OP_NOTEXACT && *code != OP_NOTEXACTI) fprintf(f, "0,");
- fprintf(f, "%d}", GET2(code,1));
- if (*code == OP_NOTMINUPTO || *code == OP_NOTMINUPTOI) fprintf(f, "?");
- else
- if (*code == OP_NOTPOSUPTO || *code == OP_NOTPOSUPTOI) fprintf(f, "+");
- break;
-
- case OP_RECURSE:
- if (print_lengths) fprintf(f, "%3d ", GET(code, 1));
- else fprintf(f, " ");
- fprintf(f, "%s", OP_names[*code]);
- break;
-
- case OP_REFI:
- flag = "/i";
- /* Fall through */
- case OP_REF:
- fprintf(f, " %s \\%d", flag, GET2(code,1));
- ccode = code + OP_lengths[*code];
- goto CLASS_REF_REPEAT;
-
- case OP_DNREFI:
- flag = "/i";
- /* Fall through */
- case OP_DNREF:
- {
- PCRE2_SPTR entry = nametable + (GET2(code, 1) * nesize) + IMM2_SIZE;
- fprintf(f, " %s \\k<", flag);
- print_custring(f, entry);
- fprintf(f, ">%d", GET2(code, 1 + IMM2_SIZE));
- }
- ccode = code + OP_lengths[*code];
- goto CLASS_REF_REPEAT;
-
- case OP_CALLOUT:
- fprintf(f, " %s %d %d %d", OP_names[*code], code[1 + 2*LINK_SIZE],
- GET(code, 1), GET(code, 1 + LINK_SIZE));
- break;
-
- case OP_CALLOUT_STR:
- c = code[1 + 4*LINK_SIZE];
- fprintf(f, " %s %c", OP_names[*code], c);
- extra = GET(code, 1 + 2*LINK_SIZE);
- print_custring_bylen(f, code + 2 + 4*LINK_SIZE, extra - 3 - 4*LINK_SIZE);
- for (i = 0; PRIV(callout_start_delims)[i] != 0; i++)
- if (c == PRIV(callout_start_delims)[i])
- {
- c = PRIV(callout_end_delims)[i];
- break;
- }
- fprintf(f, "%c %d %d %d", c, GET(code, 1 + 3*LINK_SIZE), GET(code, 1),
- GET(code, 1 + LINK_SIZE));
- break;
-
- case OP_PROP:
- case OP_NOTPROP:
- print_prop(f, code, " ", "");
- break;
-
- /* OP_XCLASS cannot occur in 8-bit, non-UTF mode. However, there's no harm
- in having this code always here, and it makes it less messy without all
- those #ifdefs. */
-
- case OP_CLASS:
- case OP_NCLASS:
- case OP_XCLASS:
- {
- unsigned int min, max;
- BOOL printmap;
- BOOL invertmap = FALSE;
- uint8_t *map;
- uint8_t inverted_map[32];
-
- fprintf(f, " [");
-
- if (*code == OP_XCLASS)
- {
- extra = GET(code, 1);
- ccode = code + LINK_SIZE + 1;
- printmap = (*ccode & XCL_MAP) != 0;
- if ((*ccode & XCL_NOT) != 0)
- {
- invertmap = (*ccode & XCL_HASPROP) == 0;
- fprintf(f, "^");
- }
- ccode++;
- }
- else
- {
- printmap = TRUE;
- ccode = code + 1;
- }
-
- /* Print a bit map */
-
- if (printmap)
- {
- map = (uint8_t *)ccode;
- if (invertmap)
- {
- for (i = 0; i < 32; i++) inverted_map[i] = ~map[i];
- map = inverted_map;
- }
-
- for (i = 0; i < 256; i++)
- {
- if ((map[i/8] & (1 << (i&7))) != 0)
- {
- int j;
- for (j = i+1; j < 256; j++)
- if ((map[j/8] & (1 << (j&7))) == 0) break;
- if (i == '-' || i == ']') fprintf(f, "\\");
- if (PRINTABLE(i)) fprintf(f, "%c", i);
- else fprintf(f, "\\x%02x", i);
- if (--j > i)
- {
- if (j != i + 1) fprintf(f, "-");
- if (j == '-' || j == ']') fprintf(f, "\\");
- if (PRINTABLE(j)) fprintf(f, "%c", j);
- else fprintf(f, "\\x%02x", j);
- }
- i = j;
- }
- }
- ccode += 32 / sizeof(PCRE2_UCHAR);
- }
-
- /* For an XCLASS there is always some additional data */
-
- if (*code == OP_XCLASS)
- {
- PCRE2_UCHAR ch;
- while ((ch = *ccode++) != XCL_END)
- {
- BOOL not = FALSE;
- const char *notch = "";
-
- switch(ch)
- {
- case XCL_NOTPROP:
- not = TRUE;
- notch = "^";
- /* Fall through */
-
- case XCL_PROP:
- {
- unsigned int ptype = *ccode++;
- unsigned int pvalue = *ccode++;
-
- switch(ptype)
- {
- case PT_PXGRAPH:
- fprintf(f, "[:%sgraph:]", notch);
- break;
-
- case PT_PXPRINT:
- fprintf(f, "[:%sprint:]", notch);
- break;
-
- case PT_PXPUNCT:
- fprintf(f, "[:%spunct:]", notch);
- break;
-
- default:
- fprintf(f, "\\%c{%s}", (not? 'P':'p'),
- get_ucpname(ptype, pvalue));
- break;
- }
- }
- break;
-
- default:
- ccode += 1 + print_char(f, ccode, utf);
- if (ch == XCL_RANGE)
- {
- fprintf(f, "-");
- ccode += 1 + print_char(f, ccode, utf);
- }
- break;
- }
- }
- }
-
- /* Indicate a non-UTF class which was created by negation */
-
- fprintf(f, "]%s", (*code == OP_NCLASS)? " (neg)" : "");
-
- /* Handle repeats after a class or a back reference */
-
- CLASS_REF_REPEAT:
- switch(*ccode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- case OP_CRPOSSTAR:
- case OP_CRPOSPLUS:
- case OP_CRPOSQUERY:
- fprintf(f, "%s", OP_names[*ccode]);
- extra += OP_lengths[*ccode];
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- case OP_CRPOSRANGE:
- min = GET2(ccode,1);
- max = GET2(ccode,1 + IMM2_SIZE);
- if (max == 0) fprintf(f, "{%u,}", min);
- else fprintf(f, "{%u,%u}", min, max);
- if (*ccode == OP_CRMINRANGE) fprintf(f, "?");
- else if (*ccode == OP_CRPOSRANGE) fprintf(f, "+");
- extra += OP_lengths[*ccode];
- break;
-
- /* Do nothing if it's not a repeat; this code stops picky compilers
- warning about the lack of a default code path. */
-
- default:
- break;
- }
- }
- break;
-
- case OP_MARK:
- case OP_PRUNE_ARG:
- case OP_SKIP_ARG:
- case OP_THEN_ARG:
- fprintf(f, " %s ", OP_names[*code]);
- print_custring_bylen(f, code + 2, code[1]);
- extra += code[1];
- break;
-
- case OP_THEN:
- fprintf(f, " %s", OP_names[*code]);
- break;
-
- case OP_CIRCM:
- case OP_DOLLM:
- flag = "/m";
- /* Fall through */
-
- /* Anything else is just an item with no data, but possibly a flag. */
-
- default:
- fprintf(f, " %s %s", flag, OP_names[*code]);
- break;
- }
-
- code += OP_lengths[*code] + extra;
- fprintf(f, "\n");
- }
-}
-
-/* End of pcre2_printint.c */
diff --git a/thirdparty/pcre2/src/pcre2_serialize.c b/thirdparty/pcre2/src/pcre2_serialize.c
index 0af26d8fc3..d2cc603cbb 100644
--- a/thirdparty/pcre2/src/pcre2_serialize.c
+++ b/thirdparty/pcre2/src/pcre2_serialize.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -214,7 +214,10 @@ for (i = 0; i < number_of_codes; i++)
if (dst_re->magic_number != MAGIC_NUMBER ||
dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 ||
dst_re->name_count > MAX_NAME_COUNT)
+ {
+ memctl->free(dst_re, memctl->memory_data);
return PCRE2_ERROR_BADSERIALIZEDDATA;
+ }
/* At the moment only one table is supported. */
diff --git a/thirdparty/pcre2/src/pcre2_study.c b/thirdparty/pcre2/src/pcre2_study.c
index 5a4d520c09..b92686759d 100644
--- a/thirdparty/pcre2/src/pcre2_study.c
+++ b/thirdparty/pcre2/src/pcre2_study.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -46,10 +46,8 @@ collecting data (e.g. minimum matching length). */
#include "config.h"
#endif
-
#include "pcre2_internal.h"
-
/* The maximum remembered capturing brackets minimum. */
#define MAX_CACHE_BACKREF 128
@@ -158,12 +156,12 @@ for (;;)
}
goto PROCESS_NON_CAPTURE;
- /* There's a special case of OP_ONCE, when it is wrapped round an
+ case OP_BRA:
+ /* There's a special case of OP_BRA, when it is wrapped round a repeated
OP_RECURSE. We'd like to process the latter at this level so that
remembering the value works for repeated cases. So we do nothing, but
set a fudge value to skip over the OP_KET after the recurse. */
- case OP_ONCE:
if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET)
{
once_fudge = 1 + LINK_SIZE;
@@ -172,8 +170,7 @@ for (;;)
}
/* Fall through */
- case OP_ONCE_NC:
- case OP_BRA:
+ case OP_ONCE:
case OP_SBRA:
case OP_BRAPOS:
case OP_SBRAPOS:
@@ -789,6 +786,7 @@ if (utf)
if (caseless)
{
+#ifdef SUPPORT_UNICODE
if (utf)
{
#if PCRE2_CODE_UNIT_WIDTH == 8
@@ -801,10 +799,12 @@ if (caseless)
if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
#endif
}
+ else
+#endif /* SUPPORT_UNICODE */
/* Not UTF */
- else if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);
+ if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);
}
return p;
@@ -953,7 +953,6 @@ do
case OP_ALLANY:
case OP_ANY:
case OP_ANYBYTE:
- case OP_CIRC:
case OP_CIRCM:
case OP_CLOSE:
case OP_COMMIT:
@@ -1021,6 +1020,13 @@ do
case OP_THEN_ARG:
return SSB_FAIL;
+ /* OP_CIRC happens only at the start of an anchored branch (multiline ^
+ uses OP_CIRCM). Skip over it. */
+
+ case OP_CIRC:
+ tcode += PRIV(OP_lengths)[OP_CIRC];
+ break;
+
/* A "real" property test implies no starting bits, but the fake property
PT_CLIST identifies a list of characters. These lists are short, as they
are used for characters with more than one "other case", so there is no
@@ -1067,7 +1073,6 @@ do
case OP_CBRAPOS:
case OP_SCBRAPOS:
case OP_ONCE:
- case OP_ONCE_NC:
case OP_ASSERT:
rc = set_start_bits(re, tcode, utf);
if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc;
@@ -1449,6 +1454,10 @@ do
classmap = ((tcode[1 + LINK_SIZE] & XCL_MAP) == 0)? NULL :
(uint8_t *)(tcode + 1 + LINK_SIZE + 1);
#endif
+ /* It seems that the fall through comment must be outside the #ifdef if
+ it is to avoid the gcc compiler warning. */
+
+ /* Fall through */
/* Enter here for a negative non-XCLASS. In the 8-bit library, if we are
in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter
@@ -1576,12 +1585,11 @@ BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
re->name_entry_size * re->name_count;
-/* For an anchored pattern, or an unanchored pattern that has a first code
-unit, or a multiline pattern that matches only at "line start", there is no
-point in seeking a list of starting code units. */
+/* For a pattern that has a first code unit, or a multiline pattern that
+matches only at "line start", there is no point in seeking a list of starting
+code units. */
-if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
- (re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)
+if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)
{
int rc = set_start_bits(re, code, utf);
if (rc == SSB_UNKNOWN) return 1;
diff --git a/thirdparty/pcre2/src/pcre2_substring.c b/thirdparty/pcre2/src/pcre2_substring.c
index f6d7c39722..ddf5774e15 100644
--- a/thirdparty/pcre2/src/pcre2_substring.c
+++ b/thirdparty/pcre2/src/pcre2_substring.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -414,7 +414,12 @@ else
for (i = 0; i < count2; i += 2)
{
size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0;
- memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size));
+
+ /* Size == 0 includes the case when the capture is unset. Avoid adding
+ PCRE2_UNSET to match_data->subject because it overflows, even though with
+ zero size calling memcpy() is harmless. */
+
+ if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size));
*listp++ = sp;
if (lensp != NULL) *lensp++ = size;
sp += size;
diff --git a/thirdparty/pcre2/src/pcre2_tables.c b/thirdparty/pcre2/src/pcre2_tables.c
index b945ed7a7f..9f8dc293aa 100644
--- a/thirdparty/pcre2/src/pcre2_tables.c
+++ b/thirdparty/pcre2/src/pcre2_tables.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/
/* This module contains some fixed tables that are used by more than one of the
-PCRE code modules. The tables are also #included by the pcre2test program,
+PCRE2 code modules. The tables are also #included by the pcre2test program,
which uses macros to change their names from _pcre2_xxx to xxxx, thereby
avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is
defined. */
@@ -148,7 +148,7 @@ two code points. The breaking rules are as follows:
1. Break at the start and end of text (pretty obviously).
-2. Do not break between a CR and LF; otherwise, break before and after
+2. Do not break between a CR and LF; otherwise, break before and after
controls.
3. Do not break Hangul syllable sequences, the rules for which are:
@@ -157,44 +157,62 @@ two code points. The breaking rules are as follows:
LV or V may be followed by V or T
LVT or T may be followed by T
-4. Do not break before extending characters.
+4. Do not break before extending characters or zero-width-joiner (ZWJ).
-The next two rules are only for extended grapheme clusters (but that's what we
+The following rules are only for extended grapheme clusters (but that's what we
are implementing).
5. Do not break before SpacingMarks.
6. Do not break after Prepend characters.
-7. Otherwise, break everywhere.
+7. Do not break within emoji modifier sequences (E_Base or E_Base_GAZ followed
+ by E_Modifier). Extend characters are allowed before the modifier; this
+ cannot be represented in this table, the code has to deal with it.
+
+8. Do not break within emoji zwj sequences (ZWJ followed by Glue_After_Zwj or
+ E_Base_GAZ).
+
+9. Do not break within emoji flag sequences. That is, do not break between
+ regional indicator (RI) symbols if there are an odd number of RI characters
+ before the break point. This table encodes "join RI characters"; the code
+ has to deal with checking for previous adjoining RIs.
+
+10. Otherwise, break everywhere.
*/
+#define ESZ (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbZWJ)
+
const uint32_t PRIV(ucp_gbtable)[] = {
(1<<ucp_gbLF), /* 0 CR */
0, /* 1 LF */
0, /* 2 Control */
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark), /* 3 Extend */
- (1<<ucp_gbExtend)|(1<<ucp_gbPrepend)| /* 4 Prepend */
- (1<<ucp_gbSpacingMark)|(1<<ucp_gbL)|
- (1<<ucp_gbV)|(1<<ucp_gbT)|(1<<ucp_gbLV)|
- (1<<ucp_gbLVT)|(1<<ucp_gbOther),
-
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark), /* 5 SpacingMark */
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbL)| /* 6 L */
- (1<<ucp_gbL)|(1<<ucp_gbV)|(1<<ucp_gbLV)|(1<<ucp_gbLVT),
-
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbV)| /* 7 V */
- (1<<ucp_gbT),
-
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbT), /* 8 T */
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbV)| /* 9 LV */
- (1<<ucp_gbT),
-
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbT), /* 10 LVT */
+ ESZ, /* 3 Extend */
+ ESZ|(1<<ucp_gbPrepend)| /* 4 Prepend */
+ (1<<ucp_gbL)|(1<<ucp_gbV)|(1<<ucp_gbT)|
+ (1<<ucp_gbLV)|(1<<ucp_gbLVT)|(1<<ucp_gbOther)|
+ (1<<ucp_gbRegionalIndicator)|
+ (1<<ucp_gbE_Base)|(1<<ucp_gbE_Modifier)|
+ (1<<ucp_gbE_Base_GAZ)|
+ (1<<ucp_gbZWJ)|(1<<ucp_gbGlue_After_Zwj),
+ ESZ, /* 5 SpacingMark */
+ ESZ|(1<<ucp_gbL)|(1<<ucp_gbV)|(1<<ucp_gbLV)| /* 6 L */
+ (1<<ucp_gbLVT),
+ ESZ|(1<<ucp_gbV)|(1<<ucp_gbT), /* 7 V */
+ ESZ|(1<<ucp_gbT), /* 8 T */
+ ESZ|(1<<ucp_gbV)|(1<<ucp_gbT), /* 9 LV */
+ ESZ|(1<<ucp_gbT), /* 10 LVT */
(1<<ucp_gbRegionalIndicator), /* 11 RegionalIndicator */
- (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark) /* 12 Other */
+ ESZ, /* 12 Other */
+ ESZ|(1<<ucp_gbE_Modifier), /* 13 E_Base */
+ ESZ, /* 14 E_Modifier */
+ ESZ|(1<<ucp_gbE_Modifier), /* 15 E_Base_GAZ */
+ ESZ|(1<<ucp_gbGlue_After_Zwj)|(1<<ucp_gbE_Base_GAZ), /* 16 ZWJ */
+ ESZ /* 12 Glue_After_Zwj */
};
+#undef ESZ
+
#ifdef SUPPORT_JIT
/* This table reverses PRIV(ucp_gentype). We can save the cost
of a memory load. */
@@ -227,6 +245,7 @@ version. Like all other character and string literals that are compared against
the regular expression pattern, we must use STR_ macros instead of literal
strings to make sure that UTF-8 support works on EBCDIC platforms. */
+#define STRING_Adlam0 STR_A STR_d STR_l STR_a STR_m "\0"
#define STRING_Ahom0 STR_A STR_h STR_o STR_m "\0"
#define STRING_Anatolian_Hieroglyphs0 STR_A STR_n STR_a STR_t STR_o STR_l STR_i STR_a STR_n STR_UNDERSCORE STR_H STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
#define STRING_Any0 STR_A STR_n STR_y "\0"
@@ -238,6 +257,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Bassa_Vah0 STR_B STR_a STR_s STR_s STR_a STR_UNDERSCORE STR_V STR_a STR_h "\0"
#define STRING_Batak0 STR_B STR_a STR_t STR_a STR_k "\0"
#define STRING_Bengali0 STR_B STR_e STR_n STR_g STR_a STR_l STR_i "\0"
+#define STRING_Bhaiksuki0 STR_B STR_h STR_a STR_i STR_k STR_s STR_u STR_k STR_i "\0"
#define STRING_Bopomofo0 STR_B STR_o STR_p STR_o STR_m STR_o STR_f STR_o "\0"
#define STRING_Brahmi0 STR_B STR_r STR_a STR_h STR_m STR_i "\0"
#define STRING_Braille0 STR_B STR_r STR_a STR_i STR_l STR_l STR_e "\0"
@@ -313,6 +333,8 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Malayalam0 STR_M STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m "\0"
#define STRING_Mandaic0 STR_M STR_a STR_n STR_d STR_a STR_i STR_c "\0"
#define STRING_Manichaean0 STR_M STR_a STR_n STR_i STR_c STR_h STR_a STR_e STR_a STR_n "\0"
+#define STRING_Marchen0 STR_M STR_a STR_r STR_c STR_h STR_e STR_n "\0"
+#define STRING_Masaram_Gondi0 STR_M STR_a STR_s STR_a STR_r STR_a STR_m STR_UNDERSCORE STR_G STR_o STR_n STR_d STR_i "\0"
#define STRING_Mc0 STR_M STR_c "\0"
#define STRING_Me0 STR_M STR_e "\0"
#define STRING_Meetei_Mayek0 STR_M STR_e STR_e STR_t STR_e STR_i STR_UNDERSCORE STR_M STR_a STR_y STR_e STR_k "\0"
@@ -330,9 +352,11 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Nabataean0 STR_N STR_a STR_b STR_a STR_t STR_a STR_e STR_a STR_n "\0"
#define STRING_Nd0 STR_N STR_d "\0"
#define STRING_New_Tai_Lue0 STR_N STR_e STR_w STR_UNDERSCORE STR_T STR_a STR_i STR_UNDERSCORE STR_L STR_u STR_e "\0"
+#define STRING_Newa0 STR_N STR_e STR_w STR_a "\0"
#define STRING_Nko0 STR_N STR_k STR_o "\0"
#define STRING_Nl0 STR_N STR_l "\0"
#define STRING_No0 STR_N STR_o "\0"
+#define STRING_Nushu0 STR_N STR_u STR_s STR_h STR_u "\0"
#define STRING_Ogham0 STR_O STR_g STR_h STR_a STR_m "\0"
#define STRING_Ol_Chiki0 STR_O STR_l STR_UNDERSCORE STR_C STR_h STR_i STR_k STR_i "\0"
#define STRING_Old_Hungarian0 STR_O STR_l STR_d STR_UNDERSCORE STR_H STR_u STR_n STR_g STR_a STR_r STR_i STR_a STR_n "\0"
@@ -343,6 +367,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Old_South_Arabian0 STR_O STR_l STR_d STR_UNDERSCORE STR_S STR_o STR_u STR_t STR_h STR_UNDERSCORE STR_A STR_r STR_a STR_b STR_i STR_a STR_n "\0"
#define STRING_Old_Turkic0 STR_O STR_l STR_d STR_UNDERSCORE STR_T STR_u STR_r STR_k STR_i STR_c "\0"
#define STRING_Oriya0 STR_O STR_r STR_i STR_y STR_a "\0"
+#define STRING_Osage0 STR_O STR_s STR_a STR_g STR_e "\0"
#define STRING_Osmanya0 STR_O STR_s STR_m STR_a STR_n STR_y STR_a "\0"
#define STRING_P0 STR_P "\0"
#define STRING_Pahawh_Hmong0 STR_P STR_a STR_h STR_a STR_w STR_h STR_UNDERSCORE STR_H STR_m STR_o STR_n STR_g "\0"
@@ -373,6 +398,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Sm0 STR_S STR_m "\0"
#define STRING_So0 STR_S STR_o "\0"
#define STRING_Sora_Sompeng0 STR_S STR_o STR_r STR_a STR_UNDERSCORE STR_S STR_o STR_m STR_p STR_e STR_n STR_g "\0"
+#define STRING_Soyombo0 STR_S STR_o STR_y STR_o STR_m STR_b STR_o "\0"
#define STRING_Sundanese0 STR_S STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e "\0"
#define STRING_Syloti_Nagri0 STR_S STR_y STR_l STR_o STR_t STR_i STR_UNDERSCORE STR_N STR_a STR_g STR_r STR_i "\0"
#define STRING_Syriac0 STR_S STR_y STR_r STR_i STR_a STR_c "\0"
@@ -383,6 +409,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Tai_Viet0 STR_T STR_a STR_i STR_UNDERSCORE STR_V STR_i STR_e STR_t "\0"
#define STRING_Takri0 STR_T STR_a STR_k STR_r STR_i "\0"
#define STRING_Tamil0 STR_T STR_a STR_m STR_i STR_l "\0"
+#define STRING_Tangut0 STR_T STR_a STR_n STR_g STR_u STR_t "\0"
#define STRING_Telugu0 STR_T STR_e STR_l STR_u STR_g STR_u "\0"
#define STRING_Thaana0 STR_T STR_h STR_a STR_a STR_n STR_a "\0"
#define STRING_Thai0 STR_T STR_h STR_a STR_i "\0"
@@ -399,11 +426,13 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */
#define STRING_Xwd0 STR_X STR_w STR_d "\0"
#define STRING_Yi0 STR_Y STR_i "\0"
#define STRING_Z0 STR_Z "\0"
+#define STRING_Zanabazar_Square0 STR_Z STR_a STR_n STR_a STR_b STR_a STR_z STR_a STR_r STR_UNDERSCORE STR_S STR_q STR_u STR_a STR_r STR_e "\0"
#define STRING_Zl0 STR_Z STR_l "\0"
#define STRING_Zp0 STR_Z STR_p "\0"
#define STRING_Zs0 STR_Z STR_s "\0"
const char PRIV(utt_names)[] =
+ STRING_Adlam0
STRING_Ahom0
STRING_Anatolian_Hieroglyphs0
STRING_Any0
@@ -415,6 +444,7 @@ const char PRIV(utt_names)[] =
STRING_Bassa_Vah0
STRING_Batak0
STRING_Bengali0
+ STRING_Bhaiksuki0
STRING_Bopomofo0
STRING_Brahmi0
STRING_Braille0
@@ -490,6 +520,8 @@ const char PRIV(utt_names)[] =
STRING_Malayalam0
STRING_Mandaic0
STRING_Manichaean0
+ STRING_Marchen0
+ STRING_Masaram_Gondi0
STRING_Mc0
STRING_Me0
STRING_Meetei_Mayek0
@@ -507,9 +539,11 @@ const char PRIV(utt_names)[] =
STRING_Nabataean0
STRING_Nd0
STRING_New_Tai_Lue0
+ STRING_Newa0
STRING_Nko0
STRING_Nl0
STRING_No0
+ STRING_Nushu0
STRING_Ogham0
STRING_Ol_Chiki0
STRING_Old_Hungarian0
@@ -520,6 +554,7 @@ const char PRIV(utt_names)[] =
STRING_Old_South_Arabian0
STRING_Old_Turkic0
STRING_Oriya0
+ STRING_Osage0
STRING_Osmanya0
STRING_P0
STRING_Pahawh_Hmong0
@@ -550,6 +585,7 @@ const char PRIV(utt_names)[] =
STRING_Sm0
STRING_So0
STRING_Sora_Sompeng0
+ STRING_Soyombo0
STRING_Sundanese0
STRING_Syloti_Nagri0
STRING_Syriac0
@@ -560,6 +596,7 @@ const char PRIV(utt_names)[] =
STRING_Tai_Viet0
STRING_Takri0
STRING_Tamil0
+ STRING_Tangut0
STRING_Telugu0
STRING_Thaana0
STRING_Thai0
@@ -576,186 +613,197 @@ const char PRIV(utt_names)[] =
STRING_Xwd0
STRING_Yi0
STRING_Z0
+ STRING_Zanabazar_Square0
STRING_Zl0
STRING_Zp0
STRING_Zs0;
const ucp_type_table PRIV(utt)[] = {
- { 0, PT_SC, ucp_Ahom },
- { 5, PT_SC, ucp_Anatolian_Hieroglyphs },
- { 27, PT_ANY, 0 },
- { 31, PT_SC, ucp_Arabic },
- { 38, PT_SC, ucp_Armenian },
- { 47, PT_SC, ucp_Avestan },
- { 55, PT_SC, ucp_Balinese },
- { 64, PT_SC, ucp_Bamum },
- { 70, PT_SC, ucp_Bassa_Vah },
- { 80, PT_SC, ucp_Batak },
- { 86, PT_SC, ucp_Bengali },
- { 94, PT_SC, ucp_Bopomofo },
- { 103, PT_SC, ucp_Brahmi },
- { 110, PT_SC, ucp_Braille },
- { 118, PT_SC, ucp_Buginese },
- { 127, PT_SC, ucp_Buhid },
- { 133, PT_GC, ucp_C },
- { 135, PT_SC, ucp_Canadian_Aboriginal },
- { 155, PT_SC, ucp_Carian },
- { 162, PT_SC, ucp_Caucasian_Albanian },
- { 181, PT_PC, ucp_Cc },
- { 184, PT_PC, ucp_Cf },
- { 187, PT_SC, ucp_Chakma },
- { 194, PT_SC, ucp_Cham },
- { 199, PT_SC, ucp_Cherokee },
- { 208, PT_PC, ucp_Cn },
- { 211, PT_PC, ucp_Co },
- { 214, PT_SC, ucp_Common },
- { 221, PT_SC, ucp_Coptic },
- { 228, PT_PC, ucp_Cs },
- { 231, PT_SC, ucp_Cuneiform },
- { 241, PT_SC, ucp_Cypriot },
- { 249, PT_SC, ucp_Cyrillic },
- { 258, PT_SC, ucp_Deseret },
- { 266, PT_SC, ucp_Devanagari },
- { 277, PT_SC, ucp_Duployan },
- { 286, PT_SC, ucp_Egyptian_Hieroglyphs },
- { 307, PT_SC, ucp_Elbasan },
- { 315, PT_SC, ucp_Ethiopic },
- { 324, PT_SC, ucp_Georgian },
- { 333, PT_SC, ucp_Glagolitic },
- { 344, PT_SC, ucp_Gothic },
- { 351, PT_SC, ucp_Grantha },
- { 359, PT_SC, ucp_Greek },
- { 365, PT_SC, ucp_Gujarati },
- { 374, PT_SC, ucp_Gurmukhi },
- { 383, PT_SC, ucp_Han },
- { 387, PT_SC, ucp_Hangul },
- { 394, PT_SC, ucp_Hanunoo },
- { 402, PT_SC, ucp_Hatran },
- { 409, PT_SC, ucp_Hebrew },
- { 416, PT_SC, ucp_Hiragana },
- { 425, PT_SC, ucp_Imperial_Aramaic },
- { 442, PT_SC, ucp_Inherited },
- { 452, PT_SC, ucp_Inscriptional_Pahlavi },
- { 474, PT_SC, ucp_Inscriptional_Parthian },
- { 497, PT_SC, ucp_Javanese },
- { 506, PT_SC, ucp_Kaithi },
- { 513, PT_SC, ucp_Kannada },
- { 521, PT_SC, ucp_Katakana },
- { 530, PT_SC, ucp_Kayah_Li },
- { 539, PT_SC, ucp_Kharoshthi },
- { 550, PT_SC, ucp_Khmer },
- { 556, PT_SC, ucp_Khojki },
- { 563, PT_SC, ucp_Khudawadi },
- { 573, PT_GC, ucp_L },
- { 575, PT_LAMP, 0 },
- { 578, PT_SC, ucp_Lao },
- { 582, PT_SC, ucp_Latin },
- { 588, PT_SC, ucp_Lepcha },
- { 595, PT_SC, ucp_Limbu },
- { 601, PT_SC, ucp_Linear_A },
- { 610, PT_SC, ucp_Linear_B },
- { 619, PT_SC, ucp_Lisu },
- { 624, PT_PC, ucp_Ll },
- { 627, PT_PC, ucp_Lm },
- { 630, PT_PC, ucp_Lo },
- { 633, PT_PC, ucp_Lt },
- { 636, PT_PC, ucp_Lu },
- { 639, PT_SC, ucp_Lycian },
- { 646, PT_SC, ucp_Lydian },
- { 653, PT_GC, ucp_M },
- { 655, PT_SC, ucp_Mahajani },
- { 664, PT_SC, ucp_Malayalam },
- { 674, PT_SC, ucp_Mandaic },
- { 682, PT_SC, ucp_Manichaean },
- { 693, PT_PC, ucp_Mc },
- { 696, PT_PC, ucp_Me },
- { 699, PT_SC, ucp_Meetei_Mayek },
- { 712, PT_SC, ucp_Mende_Kikakui },
- { 726, PT_SC, ucp_Meroitic_Cursive },
- { 743, PT_SC, ucp_Meroitic_Hieroglyphs },
- { 764, PT_SC, ucp_Miao },
- { 769, PT_PC, ucp_Mn },
- { 772, PT_SC, ucp_Modi },
- { 777, PT_SC, ucp_Mongolian },
- { 787, PT_SC, ucp_Mro },
- { 791, PT_SC, ucp_Multani },
- { 799, PT_SC, ucp_Myanmar },
- { 807, PT_GC, ucp_N },
- { 809, PT_SC, ucp_Nabataean },
- { 819, PT_PC, ucp_Nd },
- { 822, PT_SC, ucp_New_Tai_Lue },
- { 834, PT_SC, ucp_Nko },
- { 838, PT_PC, ucp_Nl },
- { 841, PT_PC, ucp_No },
- { 844, PT_SC, ucp_Ogham },
- { 850, PT_SC, ucp_Ol_Chiki },
- { 859, PT_SC, ucp_Old_Hungarian },
- { 873, PT_SC, ucp_Old_Italic },
- { 884, PT_SC, ucp_Old_North_Arabian },
- { 902, PT_SC, ucp_Old_Permic },
- { 913, PT_SC, ucp_Old_Persian },
- { 925, PT_SC, ucp_Old_South_Arabian },
- { 943, PT_SC, ucp_Old_Turkic },
- { 954, PT_SC, ucp_Oriya },
- { 960, PT_SC, ucp_Osmanya },
- { 968, PT_GC, ucp_P },
- { 970, PT_SC, ucp_Pahawh_Hmong },
- { 983, PT_SC, ucp_Palmyrene },
- { 993, PT_SC, ucp_Pau_Cin_Hau },
- { 1005, PT_PC, ucp_Pc },
- { 1008, PT_PC, ucp_Pd },
- { 1011, PT_PC, ucp_Pe },
- { 1014, PT_PC, ucp_Pf },
- { 1017, PT_SC, ucp_Phags_Pa },
- { 1026, PT_SC, ucp_Phoenician },
- { 1037, PT_PC, ucp_Pi },
- { 1040, PT_PC, ucp_Po },
- { 1043, PT_PC, ucp_Ps },
- { 1046, PT_SC, ucp_Psalter_Pahlavi },
- { 1062, PT_SC, ucp_Rejang },
- { 1069, PT_SC, ucp_Runic },
- { 1075, PT_GC, ucp_S },
- { 1077, PT_SC, ucp_Samaritan },
- { 1087, PT_SC, ucp_Saurashtra },
- { 1098, PT_PC, ucp_Sc },
- { 1101, PT_SC, ucp_Sharada },
- { 1109, PT_SC, ucp_Shavian },
- { 1117, PT_SC, ucp_Siddham },
- { 1125, PT_SC, ucp_SignWriting },
- { 1137, PT_SC, ucp_Sinhala },
- { 1145, PT_PC, ucp_Sk },
- { 1148, PT_PC, ucp_Sm },
- { 1151, PT_PC, ucp_So },
- { 1154, PT_SC, ucp_Sora_Sompeng },
- { 1167, PT_SC, ucp_Sundanese },
- { 1177, PT_SC, ucp_Syloti_Nagri },
- { 1190, PT_SC, ucp_Syriac },
- { 1197, PT_SC, ucp_Tagalog },
- { 1205, PT_SC, ucp_Tagbanwa },
- { 1214, PT_SC, ucp_Tai_Le },
- { 1221, PT_SC, ucp_Tai_Tham },
- { 1230, PT_SC, ucp_Tai_Viet },
- { 1239, PT_SC, ucp_Takri },
- { 1245, PT_SC, ucp_Tamil },
- { 1251, PT_SC, ucp_Telugu },
- { 1258, PT_SC, ucp_Thaana },
- { 1265, PT_SC, ucp_Thai },
- { 1270, PT_SC, ucp_Tibetan },
- { 1278, PT_SC, ucp_Tifinagh },
- { 1287, PT_SC, ucp_Tirhuta },
- { 1295, PT_SC, ucp_Ugaritic },
- { 1304, PT_SC, ucp_Vai },
- { 1308, PT_SC, ucp_Warang_Citi },
- { 1320, PT_ALNUM, 0 },
- { 1324, PT_PXSPACE, 0 },
- { 1328, PT_SPACE, 0 },
- { 1332, PT_UCNC, 0 },
- { 1336, PT_WORD, 0 },
- { 1340, PT_SC, ucp_Yi },
- { 1343, PT_GC, ucp_Z },
- { 1345, PT_PC, ucp_Zl },
- { 1348, PT_PC, ucp_Zp },
- { 1351, PT_PC, ucp_Zs }
+ { 0, PT_SC, ucp_Adlam },
+ { 6, PT_SC, ucp_Ahom },
+ { 11, PT_SC, ucp_Anatolian_Hieroglyphs },
+ { 33, PT_ANY, 0 },
+ { 37, PT_SC, ucp_Arabic },
+ { 44, PT_SC, ucp_Armenian },
+ { 53, PT_SC, ucp_Avestan },
+ { 61, PT_SC, ucp_Balinese },
+ { 70, PT_SC, ucp_Bamum },
+ { 76, PT_SC, ucp_Bassa_Vah },
+ { 86, PT_SC, ucp_Batak },
+ { 92, PT_SC, ucp_Bengali },
+ { 100, PT_SC, ucp_Bhaiksuki },
+ { 110, PT_SC, ucp_Bopomofo },
+ { 119, PT_SC, ucp_Brahmi },
+ { 126, PT_SC, ucp_Braille },
+ { 134, PT_SC, ucp_Buginese },
+ { 143, PT_SC, ucp_Buhid },
+ { 149, PT_GC, ucp_C },
+ { 151, PT_SC, ucp_Canadian_Aboriginal },
+ { 171, PT_SC, ucp_Carian },
+ { 178, PT_SC, ucp_Caucasian_Albanian },
+ { 197, PT_PC, ucp_Cc },
+ { 200, PT_PC, ucp_Cf },
+ { 203, PT_SC, ucp_Chakma },
+ { 210, PT_SC, ucp_Cham },
+ { 215, PT_SC, ucp_Cherokee },
+ { 224, PT_PC, ucp_Cn },
+ { 227, PT_PC, ucp_Co },
+ { 230, PT_SC, ucp_Common },
+ { 237, PT_SC, ucp_Coptic },
+ { 244, PT_PC, ucp_Cs },
+ { 247, PT_SC, ucp_Cuneiform },
+ { 257, PT_SC, ucp_Cypriot },
+ { 265, PT_SC, ucp_Cyrillic },
+ { 274, PT_SC, ucp_Deseret },
+ { 282, PT_SC, ucp_Devanagari },
+ { 293, PT_SC, ucp_Duployan },
+ { 302, PT_SC, ucp_Egyptian_Hieroglyphs },
+ { 323, PT_SC, ucp_Elbasan },
+ { 331, PT_SC, ucp_Ethiopic },
+ { 340, PT_SC, ucp_Georgian },
+ { 349, PT_SC, ucp_Glagolitic },
+ { 360, PT_SC, ucp_Gothic },
+ { 367, PT_SC, ucp_Grantha },
+ { 375, PT_SC, ucp_Greek },
+ { 381, PT_SC, ucp_Gujarati },
+ { 390, PT_SC, ucp_Gurmukhi },
+ { 399, PT_SC, ucp_Han },
+ { 403, PT_SC, ucp_Hangul },
+ { 410, PT_SC, ucp_Hanunoo },
+ { 418, PT_SC, ucp_Hatran },
+ { 425, PT_SC, ucp_Hebrew },
+ { 432, PT_SC, ucp_Hiragana },
+ { 441, PT_SC, ucp_Imperial_Aramaic },
+ { 458, PT_SC, ucp_Inherited },
+ { 468, PT_SC, ucp_Inscriptional_Pahlavi },
+ { 490, PT_SC, ucp_Inscriptional_Parthian },
+ { 513, PT_SC, ucp_Javanese },
+ { 522, PT_SC, ucp_Kaithi },
+ { 529, PT_SC, ucp_Kannada },
+ { 537, PT_SC, ucp_Katakana },
+ { 546, PT_SC, ucp_Kayah_Li },
+ { 555, PT_SC, ucp_Kharoshthi },
+ { 566, PT_SC, ucp_Khmer },
+ { 572, PT_SC, ucp_Khojki },
+ { 579, PT_SC, ucp_Khudawadi },
+ { 589, PT_GC, ucp_L },
+ { 591, PT_LAMP, 0 },
+ { 594, PT_SC, ucp_Lao },
+ { 598, PT_SC, ucp_Latin },
+ { 604, PT_SC, ucp_Lepcha },
+ { 611, PT_SC, ucp_Limbu },
+ { 617, PT_SC, ucp_Linear_A },
+ { 626, PT_SC, ucp_Linear_B },
+ { 635, PT_SC, ucp_Lisu },
+ { 640, PT_PC, ucp_Ll },
+ { 643, PT_PC, ucp_Lm },
+ { 646, PT_PC, ucp_Lo },
+ { 649, PT_PC, ucp_Lt },
+ { 652, PT_PC, ucp_Lu },
+ { 655, PT_SC, ucp_Lycian },
+ { 662, PT_SC, ucp_Lydian },
+ { 669, PT_GC, ucp_M },
+ { 671, PT_SC, ucp_Mahajani },
+ { 680, PT_SC, ucp_Malayalam },
+ { 690, PT_SC, ucp_Mandaic },
+ { 698, PT_SC, ucp_Manichaean },
+ { 709, PT_SC, ucp_Marchen },
+ { 717, PT_SC, ucp_Masaram_Gondi },
+ { 731, PT_PC, ucp_Mc },
+ { 734, PT_PC, ucp_Me },
+ { 737, PT_SC, ucp_Meetei_Mayek },
+ { 750, PT_SC, ucp_Mende_Kikakui },
+ { 764, PT_SC, ucp_Meroitic_Cursive },
+ { 781, PT_SC, ucp_Meroitic_Hieroglyphs },
+ { 802, PT_SC, ucp_Miao },
+ { 807, PT_PC, ucp_Mn },
+ { 810, PT_SC, ucp_Modi },
+ { 815, PT_SC, ucp_Mongolian },
+ { 825, PT_SC, ucp_Mro },
+ { 829, PT_SC, ucp_Multani },
+ { 837, PT_SC, ucp_Myanmar },
+ { 845, PT_GC, ucp_N },
+ { 847, PT_SC, ucp_Nabataean },
+ { 857, PT_PC, ucp_Nd },
+ { 860, PT_SC, ucp_New_Tai_Lue },
+ { 872, PT_SC, ucp_Newa },
+ { 877, PT_SC, ucp_Nko },
+ { 881, PT_PC, ucp_Nl },
+ { 884, PT_PC, ucp_No },
+ { 887, PT_SC, ucp_Nushu },
+ { 893, PT_SC, ucp_Ogham },
+ { 899, PT_SC, ucp_Ol_Chiki },
+ { 908, PT_SC, ucp_Old_Hungarian },
+ { 922, PT_SC, ucp_Old_Italic },
+ { 933, PT_SC, ucp_Old_North_Arabian },
+ { 951, PT_SC, ucp_Old_Permic },
+ { 962, PT_SC, ucp_Old_Persian },
+ { 974, PT_SC, ucp_Old_South_Arabian },
+ { 992, PT_SC, ucp_Old_Turkic },
+ { 1003, PT_SC, ucp_Oriya },
+ { 1009, PT_SC, ucp_Osage },
+ { 1015, PT_SC, ucp_Osmanya },
+ { 1023, PT_GC, ucp_P },
+ { 1025, PT_SC, ucp_Pahawh_Hmong },
+ { 1038, PT_SC, ucp_Palmyrene },
+ { 1048, PT_SC, ucp_Pau_Cin_Hau },
+ { 1060, PT_PC, ucp_Pc },
+ { 1063, PT_PC, ucp_Pd },
+ { 1066, PT_PC, ucp_Pe },
+ { 1069, PT_PC, ucp_Pf },
+ { 1072, PT_SC, ucp_Phags_Pa },
+ { 1081, PT_SC, ucp_Phoenician },
+ { 1092, PT_PC, ucp_Pi },
+ { 1095, PT_PC, ucp_Po },
+ { 1098, PT_PC, ucp_Ps },
+ { 1101, PT_SC, ucp_Psalter_Pahlavi },
+ { 1117, PT_SC, ucp_Rejang },
+ { 1124, PT_SC, ucp_Runic },
+ { 1130, PT_GC, ucp_S },
+ { 1132, PT_SC, ucp_Samaritan },
+ { 1142, PT_SC, ucp_Saurashtra },
+ { 1153, PT_PC, ucp_Sc },
+ { 1156, PT_SC, ucp_Sharada },
+ { 1164, PT_SC, ucp_Shavian },
+ { 1172, PT_SC, ucp_Siddham },
+ { 1180, PT_SC, ucp_SignWriting },
+ { 1192, PT_SC, ucp_Sinhala },
+ { 1200, PT_PC, ucp_Sk },
+ { 1203, PT_PC, ucp_Sm },
+ { 1206, PT_PC, ucp_So },
+ { 1209, PT_SC, ucp_Sora_Sompeng },
+ { 1222, PT_SC, ucp_Soyombo },
+ { 1230, PT_SC, ucp_Sundanese },
+ { 1240, PT_SC, ucp_Syloti_Nagri },
+ { 1253, PT_SC, ucp_Syriac },
+ { 1260, PT_SC, ucp_Tagalog },
+ { 1268, PT_SC, ucp_Tagbanwa },
+ { 1277, PT_SC, ucp_Tai_Le },
+ { 1284, PT_SC, ucp_Tai_Tham },
+ { 1293, PT_SC, ucp_Tai_Viet },
+ { 1302, PT_SC, ucp_Takri },
+ { 1308, PT_SC, ucp_Tamil },
+ { 1314, PT_SC, ucp_Tangut },
+ { 1321, PT_SC, ucp_Telugu },
+ { 1328, PT_SC, ucp_Thaana },
+ { 1335, PT_SC, ucp_Thai },
+ { 1340, PT_SC, ucp_Tibetan },
+ { 1348, PT_SC, ucp_Tifinagh },
+ { 1357, PT_SC, ucp_Tirhuta },
+ { 1365, PT_SC, ucp_Ugaritic },
+ { 1374, PT_SC, ucp_Vai },
+ { 1378, PT_SC, ucp_Warang_Citi },
+ { 1390, PT_ALNUM, 0 },
+ { 1394, PT_PXSPACE, 0 },
+ { 1398, PT_SPACE, 0 },
+ { 1402, PT_UCNC, 0 },
+ { 1406, PT_WORD, 0 },
+ { 1410, PT_SC, ucp_Yi },
+ { 1413, PT_GC, ucp_Z },
+ { 1415, PT_SC, ucp_Zanabazar_Square },
+ { 1432, PT_PC, ucp_Zl },
+ { 1435, PT_PC, ucp_Zp },
+ { 1438, PT_PC, ucp_Zs }
};
const size_t PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table);
diff --git a/thirdparty/pcre2/src/pcre2_ucd.c b/thirdparty/pcre2/src/pcre2_ucd.c
index 116f537b38..ac7649b99e 100644
--- a/thirdparty/pcre2/src/pcre2_ucd.c
+++ b/thirdparty/pcre2/src/pcre2_ucd.c
@@ -20,7 +20,7 @@ needed. */
/* Unicode character database. */
/* This file was autogenerated by the MultiStage2.py script. */
-/* Total size: 75072 bytes, block size: 128. */
+/* Total size: 80808 bytes, block size: 128. */
/* The tables herein are needed only when UCP support is built,
and in PCRE2 that happens automatically with UTF support.
@@ -39,7 +39,21 @@ const uint16_t PRIV(ucd_stage2)[] = {0};
const uint32_t PRIV(ucd_caseless_sets)[] = {0};
#else
-const char *PRIV(unicode_version) = "8.0.0";
+const char *PRIV(unicode_version) = "10.0.0";
+
+/* If the 32-bit library is run in non-32-bit mode, character values
+greater than 0x10ffff may be encountered. For these we set up a
+special record. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+const ucd_record PRIV(dummy_ucd_record)[] = {{
+ ucp_Common, /* script */
+ ucp_Cn, /* type unassigned */
+ ucp_gbOther, /* grapheme break property */
+ 0, /* case set */
+ 0, /* other case */
+ }};
+#endif
/* When recompiling tables with a new Unicode version, please check the
types in this structure definition from pcre2_internal.h (the actual
@@ -72,17 +86,25 @@ const uint32_t PRIV(ucd_caseless_sets)[] = {
0x039a, 0x03ba, 0x03f0, NOTACHAR,
0x03a1, 0x03c1, 0x03f1, NOTACHAR,
0x0395, 0x03b5, 0x03f5, NOTACHAR,
+ 0x0412, 0x0432, 0x1c80, NOTACHAR,
+ 0x0414, 0x0434, 0x1c81, NOTACHAR,
+ 0x041e, 0x043e, 0x1c82, NOTACHAR,
+ 0x0421, 0x0441, 0x1c83, NOTACHAR,
+ 0x0422, 0x0442, 0x1c84, 0x1c85, NOTACHAR,
+ 0x042a, 0x044a, 0x1c86, NOTACHAR,
+ 0x0462, 0x0463, 0x1c87, NOTACHAR,
0x1e60, 0x1e61, 0x1e9b, NOTACHAR,
0x03a9, 0x03c9, 0x2126, NOTACHAR,
0x004b, 0x006b, 0x212a, NOTACHAR,
0x00c5, 0x00e5, 0x212b, NOTACHAR,
+ 0x1c88, 0xa64a, 0xa64b, NOTACHAR,
};
/* When #included in pcre2test, we don't need this large table. */
#ifndef PCRE2_PCRE2TEST
-const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
+const ucd_record PRIV(ucd_records)[] = { /* 6568 bytes, record size 8 */
{ 9, 0, 2, 0, 0, }, /* 0 */
{ 9, 0, 1, 0, 0, }, /* 1 */
{ 9, 0, 0, 0, 0, }, /* 2 */
@@ -95,12 +117,12 @@ const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
{ 9, 17, 12, 0, 0, }, /* 9 */
{ 9, 13, 12, 0, 0, }, /* 10 */
{ 33, 9, 12, 0, 32, }, /* 11 */
- { 33, 9, 12, 71, 32, }, /* 12 */
+ { 33, 9, 12, 100, 32, }, /* 12 */
{ 33, 9, 12, 1, 32, }, /* 13 */
{ 9, 24, 12, 0, 0, }, /* 14 */
{ 9, 16, 12, 0, 0, }, /* 15 */
{ 33, 5, 12, 0, -32, }, /* 16 */
- { 33, 5, 12, 71, -32, }, /* 17 */
+ { 33, 5, 12, 100, -32, }, /* 17 */
{ 33, 5, 12, 1, -32, }, /* 18 */
{ 9, 26, 12, 0, 0, }, /* 19 */
{ 33, 7, 12, 0, 0, }, /* 20 */
@@ -109,9 +131,9 @@ const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
{ 9, 15, 12, 0, 0, }, /* 23 */
{ 9, 5, 12, 26, 775, }, /* 24 */
{ 9, 19, 12, 0, 0, }, /* 25 */
- { 33, 9, 12, 75, 32, }, /* 26 */
+ { 33, 9, 12, 104, 32, }, /* 26 */
{ 33, 5, 12, 0, 7615, }, /* 27 */
- { 33, 5, 12, 75, -32, }, /* 28 */
+ { 33, 5, 12, 104, -32, }, /* 28 */
{ 33, 5, 12, 0, 121, }, /* 29 */
{ 33, 9, 12, 0, 1, }, /* 30 */
{ 33, 5, 12, 0, -1, }, /* 31 */
@@ -218,7 +240,7 @@ const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
{ 19, 9, 12, 55, 32, }, /* 132 */
{ 19, 9, 12, 30, 32, }, /* 133 */
{ 19, 9, 12, 43, 32, }, /* 134 */
- { 19, 9, 12, 67, 32, }, /* 135 */
+ { 19, 9, 12, 96, 32, }, /* 135 */
{ 19, 5, 12, 0, -38, }, /* 136 */
{ 19, 5, 12, 0, -37, }, /* 137 */
{ 19, 5, 12, 0, -32, }, /* 138 */
@@ -233,7 +255,7 @@ const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
{ 19, 5, 12, 30, 1, }, /* 147 */
{ 19, 5, 12, 30, -32, }, /* 148 */
{ 19, 5, 12, 43, -32, }, /* 149 */
- { 19, 5, 12, 67, -32, }, /* 150 */
+ { 19, 5, 12, 96, -32, }, /* 150 */
{ 19, 5, 12, 0, -64, }, /* 151 */
{ 19, 5, 12, 0, -63, }, /* 152 */
{ 19, 9, 12, 0, 8, }, /* 153 */
@@ -256,577 +278,654 @@ const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */
{ 19, 9, 12, 0, -130, }, /* 170 */
{ 12, 9, 12, 0, 80, }, /* 171 */
{ 12, 9, 12, 0, 32, }, /* 172 */
- { 12, 5, 12, 0, -32, }, /* 173 */
- { 12, 5, 12, 0, -80, }, /* 174 */
- { 12, 9, 12, 0, 1, }, /* 175 */
- { 12, 5, 12, 0, -1, }, /* 176 */
- { 12, 26, 12, 0, 0, }, /* 177 */
- { 12, 12, 3, 0, 0, }, /* 178 */
- { 12, 11, 3, 0, 0, }, /* 179 */
- { 12, 9, 12, 0, 15, }, /* 180 */
- { 12, 5, 12, 0, -15, }, /* 181 */
- { 1, 9, 12, 0, 48, }, /* 182 */
- { 1, 6, 12, 0, 0, }, /* 183 */
- { 1, 21, 12, 0, 0, }, /* 184 */
- { 1, 5, 12, 0, -48, }, /* 185 */
- { 1, 5, 12, 0, 0, }, /* 186 */
- { 1, 17, 12, 0, 0, }, /* 187 */
- { 1, 26, 12, 0, 0, }, /* 188 */
- { 1, 23, 12, 0, 0, }, /* 189 */
- { 25, 12, 3, 0, 0, }, /* 190 */
- { 25, 17, 12, 0, 0, }, /* 191 */
- { 25, 21, 12, 0, 0, }, /* 192 */
- { 25, 7, 12, 0, 0, }, /* 193 */
- { 0, 1, 2, 0, 0, }, /* 194 */
- { 0, 25, 12, 0, 0, }, /* 195 */
- { 0, 21, 12, 0, 0, }, /* 196 */
- { 0, 23, 12, 0, 0, }, /* 197 */
- { 0, 26, 12, 0, 0, }, /* 198 */
- { 0, 12, 3, 0, 0, }, /* 199 */
- { 0, 7, 12, 0, 0, }, /* 200 */
- { 0, 13, 12, 0, 0, }, /* 201 */
- { 0, 6, 12, 0, 0, }, /* 202 */
- { 49, 21, 12, 0, 0, }, /* 203 */
- { 49, 1, 2, 0, 0, }, /* 204 */
- { 49, 7, 12, 0, 0, }, /* 205 */
- { 49, 12, 3, 0, 0, }, /* 206 */
- { 55, 7, 12, 0, 0, }, /* 207 */
- { 55, 12, 3, 0, 0, }, /* 208 */
- { 63, 13, 12, 0, 0, }, /* 209 */
- { 63, 7, 12, 0, 0, }, /* 210 */
- { 63, 12, 3, 0, 0, }, /* 211 */
- { 63, 6, 12, 0, 0, }, /* 212 */
- { 63, 26, 12, 0, 0, }, /* 213 */
- { 63, 21, 12, 0, 0, }, /* 214 */
- { 89, 7, 12, 0, 0, }, /* 215 */
- { 89, 12, 3, 0, 0, }, /* 216 */
- { 89, 6, 12, 0, 0, }, /* 217 */
- { 89, 21, 12, 0, 0, }, /* 218 */
- { 94, 7, 12, 0, 0, }, /* 219 */
- { 94, 12, 3, 0, 0, }, /* 220 */
- { 94, 21, 12, 0, 0, }, /* 221 */
- { 14, 12, 3, 0, 0, }, /* 222 */
- { 14, 10, 5, 0, 0, }, /* 223 */
- { 14, 7, 12, 0, 0, }, /* 224 */
- { 14, 13, 12, 0, 0, }, /* 225 */
- { 14, 21, 12, 0, 0, }, /* 226 */
- { 14, 6, 12, 0, 0, }, /* 227 */
- { 2, 7, 12, 0, 0, }, /* 228 */
- { 2, 12, 3, 0, 0, }, /* 229 */
- { 2, 10, 5, 0, 0, }, /* 230 */
- { 2, 10, 3, 0, 0, }, /* 231 */
- { 2, 13, 12, 0, 0, }, /* 232 */
- { 2, 23, 12, 0, 0, }, /* 233 */
- { 2, 15, 12, 0, 0, }, /* 234 */
- { 2, 26, 12, 0, 0, }, /* 235 */
- { 21, 12, 3, 0, 0, }, /* 236 */
- { 21, 10, 5, 0, 0, }, /* 237 */
- { 21, 7, 12, 0, 0, }, /* 238 */
- { 21, 13, 12, 0, 0, }, /* 239 */
- { 20, 12, 3, 0, 0, }, /* 240 */
- { 20, 10, 5, 0, 0, }, /* 241 */
- { 20, 7, 12, 0, 0, }, /* 242 */
- { 20, 13, 12, 0, 0, }, /* 243 */
- { 20, 21, 12, 0, 0, }, /* 244 */
- { 20, 23, 12, 0, 0, }, /* 245 */
- { 43, 12, 3, 0, 0, }, /* 246 */
- { 43, 10, 5, 0, 0, }, /* 247 */
- { 43, 7, 12, 0, 0, }, /* 248 */
- { 43, 10, 3, 0, 0, }, /* 249 */
- { 43, 13, 12, 0, 0, }, /* 250 */
- { 43, 26, 12, 0, 0, }, /* 251 */
- { 43, 15, 12, 0, 0, }, /* 252 */
- { 53, 12, 3, 0, 0, }, /* 253 */
- { 53, 7, 12, 0, 0, }, /* 254 */
- { 53, 10, 3, 0, 0, }, /* 255 */
- { 53, 10, 5, 0, 0, }, /* 256 */
- { 53, 13, 12, 0, 0, }, /* 257 */
- { 53, 15, 12, 0, 0, }, /* 258 */
- { 53, 26, 12, 0, 0, }, /* 259 */
- { 53, 23, 12, 0, 0, }, /* 260 */
- { 54, 12, 3, 0, 0, }, /* 261 */
- { 54, 10, 5, 0, 0, }, /* 262 */
- { 54, 7, 12, 0, 0, }, /* 263 */
- { 54, 13, 12, 0, 0, }, /* 264 */
- { 54, 15, 12, 0, 0, }, /* 265 */
- { 54, 26, 12, 0, 0, }, /* 266 */
- { 28, 12, 3, 0, 0, }, /* 267 */
- { 28, 10, 5, 0, 0, }, /* 268 */
- { 28, 7, 12, 0, 0, }, /* 269 */
- { 28, 10, 3, 0, 0, }, /* 270 */
- { 28, 13, 12, 0, 0, }, /* 271 */
- { 36, 12, 3, 0, 0, }, /* 272 */
- { 36, 10, 5, 0, 0, }, /* 273 */
- { 36, 7, 12, 0, 0, }, /* 274 */
- { 36, 10, 3, 0, 0, }, /* 275 */
- { 36, 13, 12, 0, 0, }, /* 276 */
- { 36, 15, 12, 0, 0, }, /* 277 */
- { 36, 26, 12, 0, 0, }, /* 278 */
- { 47, 10, 5, 0, 0, }, /* 279 */
- { 47, 7, 12, 0, 0, }, /* 280 */
- { 47, 12, 3, 0, 0, }, /* 281 */
- { 47, 10, 3, 0, 0, }, /* 282 */
- { 47, 13, 12, 0, 0, }, /* 283 */
- { 47, 21, 12, 0, 0, }, /* 284 */
- { 56, 7, 12, 0, 0, }, /* 285 */
- { 56, 12, 3, 0, 0, }, /* 286 */
- { 56, 7, 5, 0, 0, }, /* 287 */
- { 56, 6, 12, 0, 0, }, /* 288 */
- { 56, 21, 12, 0, 0, }, /* 289 */
- { 56, 13, 12, 0, 0, }, /* 290 */
- { 32, 7, 12, 0, 0, }, /* 291 */
- { 32, 12, 3, 0, 0, }, /* 292 */
- { 32, 7, 5, 0, 0, }, /* 293 */
- { 32, 6, 12, 0, 0, }, /* 294 */
- { 32, 13, 12, 0, 0, }, /* 295 */
- { 57, 7, 12, 0, 0, }, /* 296 */
- { 57, 26, 12, 0, 0, }, /* 297 */
- { 57, 21, 12, 0, 0, }, /* 298 */
- { 57, 12, 3, 0, 0, }, /* 299 */
- { 57, 13, 12, 0, 0, }, /* 300 */
- { 57, 15, 12, 0, 0, }, /* 301 */
- { 57, 22, 12, 0, 0, }, /* 302 */
- { 57, 18, 12, 0, 0, }, /* 303 */
- { 57, 10, 5, 0, 0, }, /* 304 */
- { 38, 7, 12, 0, 0, }, /* 305 */
- { 38, 10, 12, 0, 0, }, /* 306 */
- { 38, 12, 3, 0, 0, }, /* 307 */
- { 38, 10, 5, 0, 0, }, /* 308 */
- { 38, 13, 12, 0, 0, }, /* 309 */
- { 38, 21, 12, 0, 0, }, /* 310 */
- { 38, 26, 12, 0, 0, }, /* 311 */
- { 16, 9, 12, 0, 7264, }, /* 312 */
- { 16, 7, 12, 0, 0, }, /* 313 */
- { 16, 6, 12, 0, 0, }, /* 314 */
- { 23, 7, 6, 0, 0, }, /* 315 */
- { 23, 7, 7, 0, 0, }, /* 316 */
- { 23, 7, 8, 0, 0, }, /* 317 */
- { 15, 7, 12, 0, 0, }, /* 318 */
- { 15, 12, 3, 0, 0, }, /* 319 */
- { 15, 21, 12, 0, 0, }, /* 320 */
- { 15, 15, 12, 0, 0, }, /* 321 */
- { 15, 26, 12, 0, 0, }, /* 322 */
- { 8, 9, 12, 0, 38864, }, /* 323 */
- { 8, 9, 12, 0, 8, }, /* 324 */
- { 8, 5, 12, 0, -8, }, /* 325 */
- { 7, 17, 12, 0, 0, }, /* 326 */
- { 7, 7, 12, 0, 0, }, /* 327 */
- { 7, 21, 12, 0, 0, }, /* 328 */
- { 40, 29, 12, 0, 0, }, /* 329 */
- { 40, 7, 12, 0, 0, }, /* 330 */
- { 40, 22, 12, 0, 0, }, /* 331 */
- { 40, 18, 12, 0, 0, }, /* 332 */
- { 45, 7, 12, 0, 0, }, /* 333 */
- { 45, 14, 12, 0, 0, }, /* 334 */
- { 50, 7, 12, 0, 0, }, /* 335 */
- { 50, 12, 3, 0, 0, }, /* 336 */
- { 24, 7, 12, 0, 0, }, /* 337 */
- { 24, 12, 3, 0, 0, }, /* 338 */
- { 6, 7, 12, 0, 0, }, /* 339 */
- { 6, 12, 3, 0, 0, }, /* 340 */
- { 51, 7, 12, 0, 0, }, /* 341 */
- { 51, 12, 3, 0, 0, }, /* 342 */
- { 31, 7, 12, 0, 0, }, /* 343 */
- { 31, 12, 3, 0, 0, }, /* 344 */
- { 31, 10, 5, 0, 0, }, /* 345 */
- { 31, 21, 12, 0, 0, }, /* 346 */
- { 31, 6, 12, 0, 0, }, /* 347 */
- { 31, 23, 12, 0, 0, }, /* 348 */
- { 31, 13, 12, 0, 0, }, /* 349 */
- { 31, 15, 12, 0, 0, }, /* 350 */
- { 37, 21, 12, 0, 0, }, /* 351 */
- { 37, 17, 12, 0, 0, }, /* 352 */
- { 37, 12, 3, 0, 0, }, /* 353 */
- { 37, 1, 2, 0, 0, }, /* 354 */
- { 37, 13, 12, 0, 0, }, /* 355 */
- { 37, 7, 12, 0, 0, }, /* 356 */
- { 37, 6, 12, 0, 0, }, /* 357 */
- { 34, 7, 12, 0, 0, }, /* 358 */
- { 34, 12, 3, 0, 0, }, /* 359 */
- { 34, 10, 5, 0, 0, }, /* 360 */
- { 34, 26, 12, 0, 0, }, /* 361 */
- { 34, 21, 12, 0, 0, }, /* 362 */
- { 34, 13, 12, 0, 0, }, /* 363 */
- { 52, 7, 12, 0, 0, }, /* 364 */
- { 39, 7, 12, 0, 0, }, /* 365 */
- { 39, 13, 12, 0, 0, }, /* 366 */
- { 39, 15, 12, 0, 0, }, /* 367 */
- { 39, 26, 12, 0, 0, }, /* 368 */
- { 31, 26, 12, 0, 0, }, /* 369 */
- { 5, 7, 12, 0, 0, }, /* 370 */
- { 5, 12, 3, 0, 0, }, /* 371 */
- { 5, 10, 5, 0, 0, }, /* 372 */
- { 5, 21, 12, 0, 0, }, /* 373 */
- { 90, 7, 12, 0, 0, }, /* 374 */
- { 90, 10, 5, 0, 0, }, /* 375 */
- { 90, 12, 3, 0, 0, }, /* 376 */
- { 90, 10, 12, 0, 0, }, /* 377 */
- { 90, 13, 12, 0, 0, }, /* 378 */
- { 90, 21, 12, 0, 0, }, /* 379 */
- { 90, 6, 12, 0, 0, }, /* 380 */
- { 27, 11, 3, 0, 0, }, /* 381 */
- { 61, 12, 3, 0, 0, }, /* 382 */
- { 61, 10, 5, 0, 0, }, /* 383 */
- { 61, 7, 12, 0, 0, }, /* 384 */
- { 61, 13, 12, 0, 0, }, /* 385 */
- { 61, 21, 12, 0, 0, }, /* 386 */
- { 61, 26, 12, 0, 0, }, /* 387 */
- { 75, 12, 3, 0, 0, }, /* 388 */
- { 75, 10, 5, 0, 0, }, /* 389 */
- { 75, 7, 12, 0, 0, }, /* 390 */
- { 75, 13, 12, 0, 0, }, /* 391 */
- { 92, 7, 12, 0, 0, }, /* 392 */
- { 92, 12, 3, 0, 0, }, /* 393 */
- { 92, 10, 5, 0, 0, }, /* 394 */
- { 92, 21, 12, 0, 0, }, /* 395 */
- { 69, 7, 12, 0, 0, }, /* 396 */
- { 69, 10, 5, 0, 0, }, /* 397 */
- { 69, 12, 3, 0, 0, }, /* 398 */
- { 69, 21, 12, 0, 0, }, /* 399 */
- { 69, 13, 12, 0, 0, }, /* 400 */
- { 72, 13, 12, 0, 0, }, /* 401 */
- { 72, 7, 12, 0, 0, }, /* 402 */
- { 72, 6, 12, 0, 0, }, /* 403 */
- { 72, 21, 12, 0, 0, }, /* 404 */
- { 75, 21, 12, 0, 0, }, /* 405 */
- { 9, 10, 5, 0, 0, }, /* 406 */
- { 9, 7, 12, 0, 0, }, /* 407 */
- { 12, 5, 12, 0, 0, }, /* 408 */
- { 12, 6, 12, 0, 0, }, /* 409 */
- { 33, 5, 12, 0, 35332, }, /* 410 */
- { 33, 5, 12, 0, 3814, }, /* 411 */
- { 33, 9, 12, 63, 1, }, /* 412 */
- { 33, 5, 12, 63, -1, }, /* 413 */
- { 33, 5, 12, 63, -58, }, /* 414 */
- { 33, 9, 12, 0, -7615, }, /* 415 */
- { 19, 5, 12, 0, 8, }, /* 416 */
- { 19, 9, 12, 0, -8, }, /* 417 */
- { 19, 5, 12, 0, 74, }, /* 418 */
- { 19, 5, 12, 0, 86, }, /* 419 */
- { 19, 5, 12, 0, 100, }, /* 420 */
- { 19, 5, 12, 0, 128, }, /* 421 */
- { 19, 5, 12, 0, 112, }, /* 422 */
- { 19, 5, 12, 0, 126, }, /* 423 */
- { 19, 8, 12, 0, -8, }, /* 424 */
- { 19, 5, 12, 0, 9, }, /* 425 */
- { 19, 9, 12, 0, -74, }, /* 426 */
- { 19, 8, 12, 0, -9, }, /* 427 */
- { 19, 5, 12, 21, -7173, }, /* 428 */
- { 19, 9, 12, 0, -86, }, /* 429 */
- { 19, 9, 12, 0, -100, }, /* 430 */
- { 19, 9, 12, 0, -112, }, /* 431 */
- { 19, 9, 12, 0, -128, }, /* 432 */
- { 19, 9, 12, 0, -126, }, /* 433 */
- { 27, 1, 3, 0, 0, }, /* 434 */
- { 9, 27, 2, 0, 0, }, /* 435 */
- { 9, 28, 2, 0, 0, }, /* 436 */
- { 9, 2, 2, 0, 0, }, /* 437 */
- { 9, 9, 12, 0, 0, }, /* 438 */
- { 9, 5, 12, 0, 0, }, /* 439 */
- { 19, 9, 12, 67, -7517, }, /* 440 */
- { 33, 9, 12, 71, -8383, }, /* 441 */
- { 33, 9, 12, 75, -8262, }, /* 442 */
- { 33, 9, 12, 0, 28, }, /* 443 */
- { 33, 5, 12, 0, -28, }, /* 444 */
- { 33, 14, 12, 0, 16, }, /* 445 */
- { 33, 14, 12, 0, -16, }, /* 446 */
- { 33, 14, 12, 0, 0, }, /* 447 */
- { 9, 26, 12, 0, 26, }, /* 448 */
- { 9, 26, 12, 0, -26, }, /* 449 */
- { 4, 26, 12, 0, 0, }, /* 450 */
- { 17, 9, 12, 0, 48, }, /* 451 */
- { 17, 5, 12, 0, -48, }, /* 452 */
- { 33, 9, 12, 0, -10743, }, /* 453 */
- { 33, 9, 12, 0, -3814, }, /* 454 */
- { 33, 9, 12, 0, -10727, }, /* 455 */
- { 33, 5, 12, 0, -10795, }, /* 456 */
- { 33, 5, 12, 0, -10792, }, /* 457 */
- { 33, 9, 12, 0, -10780, }, /* 458 */
- { 33, 9, 12, 0, -10749, }, /* 459 */
- { 33, 9, 12, 0, -10783, }, /* 460 */
- { 33, 9, 12, 0, -10782, }, /* 461 */
- { 33, 9, 12, 0, -10815, }, /* 462 */
- { 10, 5, 12, 0, 0, }, /* 463 */
- { 10, 26, 12, 0, 0, }, /* 464 */
- { 10, 12, 3, 0, 0, }, /* 465 */
- { 10, 21, 12, 0, 0, }, /* 466 */
- { 10, 15, 12, 0, 0, }, /* 467 */
- { 16, 5, 12, 0, -7264, }, /* 468 */
- { 58, 7, 12, 0, 0, }, /* 469 */
- { 58, 6, 12, 0, 0, }, /* 470 */
- { 58, 21, 12, 0, 0, }, /* 471 */
- { 58, 12, 3, 0, 0, }, /* 472 */
- { 22, 26, 12, 0, 0, }, /* 473 */
- { 22, 6, 12, 0, 0, }, /* 474 */
- { 22, 14, 12, 0, 0, }, /* 475 */
- { 23, 10, 3, 0, 0, }, /* 476 */
- { 26, 7, 12, 0, 0, }, /* 477 */
- { 26, 6, 12, 0, 0, }, /* 478 */
- { 29, 7, 12, 0, 0, }, /* 479 */
- { 29, 6, 12, 0, 0, }, /* 480 */
- { 3, 7, 12, 0, 0, }, /* 481 */
- { 23, 7, 12, 0, 0, }, /* 482 */
- { 23, 26, 12, 0, 0, }, /* 483 */
- { 29, 26, 12, 0, 0, }, /* 484 */
- { 22, 7, 12, 0, 0, }, /* 485 */
- { 60, 7, 12, 0, 0, }, /* 486 */
- { 60, 6, 12, 0, 0, }, /* 487 */
- { 60, 26, 12, 0, 0, }, /* 488 */
- { 85, 7, 12, 0, 0, }, /* 489 */
- { 85, 6, 12, 0, 0, }, /* 490 */
- { 85, 21, 12, 0, 0, }, /* 491 */
- { 76, 7, 12, 0, 0, }, /* 492 */
- { 76, 6, 12, 0, 0, }, /* 493 */
- { 76, 21, 12, 0, 0, }, /* 494 */
- { 76, 13, 12, 0, 0, }, /* 495 */
- { 12, 7, 12, 0, 0, }, /* 496 */
- { 12, 21, 12, 0, 0, }, /* 497 */
- { 78, 7, 12, 0, 0, }, /* 498 */
- { 78, 14, 12, 0, 0, }, /* 499 */
- { 78, 12, 3, 0, 0, }, /* 500 */
- { 78, 21, 12, 0, 0, }, /* 501 */
- { 33, 9, 12, 0, -35332, }, /* 502 */
- { 33, 9, 12, 0, -42280, }, /* 503 */
- { 33, 9, 12, 0, -42308, }, /* 504 */
- { 33, 9, 12, 0, -42319, }, /* 505 */
- { 33, 9, 12, 0, -42315, }, /* 506 */
- { 33, 9, 12, 0, -42305, }, /* 507 */
- { 33, 9, 12, 0, -42258, }, /* 508 */
- { 33, 9, 12, 0, -42282, }, /* 509 */
- { 33, 9, 12, 0, -42261, }, /* 510 */
- { 33, 9, 12, 0, 928, }, /* 511 */
- { 48, 7, 12, 0, 0, }, /* 512 */
- { 48, 12, 3, 0, 0, }, /* 513 */
- { 48, 10, 5, 0, 0, }, /* 514 */
- { 48, 26, 12, 0, 0, }, /* 515 */
- { 64, 7, 12, 0, 0, }, /* 516 */
- { 64, 21, 12, 0, 0, }, /* 517 */
- { 74, 10, 5, 0, 0, }, /* 518 */
- { 74, 7, 12, 0, 0, }, /* 519 */
- { 74, 12, 3, 0, 0, }, /* 520 */
- { 74, 21, 12, 0, 0, }, /* 521 */
- { 74, 13, 12, 0, 0, }, /* 522 */
- { 68, 13, 12, 0, 0, }, /* 523 */
- { 68, 7, 12, 0, 0, }, /* 524 */
- { 68, 12, 3, 0, 0, }, /* 525 */
- { 68, 21, 12, 0, 0, }, /* 526 */
- { 73, 7, 12, 0, 0, }, /* 527 */
- { 73, 12, 3, 0, 0, }, /* 528 */
- { 73, 10, 5, 0, 0, }, /* 529 */
- { 73, 21, 12, 0, 0, }, /* 530 */
- { 83, 12, 3, 0, 0, }, /* 531 */
- { 83, 10, 5, 0, 0, }, /* 532 */
- { 83, 7, 12, 0, 0, }, /* 533 */
- { 83, 21, 12, 0, 0, }, /* 534 */
- { 83, 13, 12, 0, 0, }, /* 535 */
- { 38, 6, 12, 0, 0, }, /* 536 */
- { 67, 7, 12, 0, 0, }, /* 537 */
- { 67, 12, 3, 0, 0, }, /* 538 */
- { 67, 10, 5, 0, 0, }, /* 539 */
- { 67, 13, 12, 0, 0, }, /* 540 */
- { 67, 21, 12, 0, 0, }, /* 541 */
- { 91, 7, 12, 0, 0, }, /* 542 */
- { 91, 12, 3, 0, 0, }, /* 543 */
- { 91, 6, 12, 0, 0, }, /* 544 */
- { 91, 21, 12, 0, 0, }, /* 545 */
- { 86, 7, 12, 0, 0, }, /* 546 */
- { 86, 10, 5, 0, 0, }, /* 547 */
- { 86, 12, 3, 0, 0, }, /* 548 */
- { 86, 21, 12, 0, 0, }, /* 549 */
- { 86, 6, 12, 0, 0, }, /* 550 */
- { 33, 5, 12, 0, -928, }, /* 551 */
- { 8, 5, 12, 0, -38864, }, /* 552 */
- { 86, 13, 12, 0, 0, }, /* 553 */
- { 23, 7, 9, 0, 0, }, /* 554 */
- { 23, 7, 10, 0, 0, }, /* 555 */
- { 9, 4, 2, 0, 0, }, /* 556 */
- { 9, 3, 12, 0, 0, }, /* 557 */
- { 25, 25, 12, 0, 0, }, /* 558 */
- { 0, 24, 12, 0, 0, }, /* 559 */
- { 9, 6, 3, 0, 0, }, /* 560 */
- { 35, 7, 12, 0, 0, }, /* 561 */
- { 19, 14, 12, 0, 0, }, /* 562 */
- { 19, 15, 12, 0, 0, }, /* 563 */
- { 19, 26, 12, 0, 0, }, /* 564 */
- { 70, 7, 12, 0, 0, }, /* 565 */
- { 66, 7, 12, 0, 0, }, /* 566 */
- { 41, 7, 12, 0, 0, }, /* 567 */
- { 41, 15, 12, 0, 0, }, /* 568 */
- { 18, 7, 12, 0, 0, }, /* 569 */
- { 18, 14, 12, 0, 0, }, /* 570 */
- { 117, 7, 12, 0, 0, }, /* 571 */
- { 117, 12, 3, 0, 0, }, /* 572 */
- { 59, 7, 12, 0, 0, }, /* 573 */
- { 59, 21, 12, 0, 0, }, /* 574 */
- { 42, 7, 12, 0, 0, }, /* 575 */
- { 42, 21, 12, 0, 0, }, /* 576 */
- { 42, 14, 12, 0, 0, }, /* 577 */
- { 13, 9, 12, 0, 40, }, /* 578 */
- { 13, 5, 12, 0, -40, }, /* 579 */
- { 46, 7, 12, 0, 0, }, /* 580 */
- { 44, 7, 12, 0, 0, }, /* 581 */
- { 44, 13, 12, 0, 0, }, /* 582 */
- { 105, 7, 12, 0, 0, }, /* 583 */
- { 103, 7, 12, 0, 0, }, /* 584 */
- { 103, 21, 12, 0, 0, }, /* 585 */
- { 109, 7, 12, 0, 0, }, /* 586 */
- { 11, 7, 12, 0, 0, }, /* 587 */
- { 80, 7, 12, 0, 0, }, /* 588 */
- { 80, 21, 12, 0, 0, }, /* 589 */
- { 80, 15, 12, 0, 0, }, /* 590 */
- { 119, 7, 12, 0, 0, }, /* 591 */
- { 119, 26, 12, 0, 0, }, /* 592 */
- { 119, 15, 12, 0, 0, }, /* 593 */
- { 115, 7, 12, 0, 0, }, /* 594 */
- { 115, 15, 12, 0, 0, }, /* 595 */
- { 127, 7, 12, 0, 0, }, /* 596 */
- { 127, 15, 12, 0, 0, }, /* 597 */
- { 65, 7, 12, 0, 0, }, /* 598 */
- { 65, 15, 12, 0, 0, }, /* 599 */
- { 65, 21, 12, 0, 0, }, /* 600 */
- { 71, 7, 12, 0, 0, }, /* 601 */
- { 71, 21, 12, 0, 0, }, /* 602 */
- { 97, 7, 12, 0, 0, }, /* 603 */
- { 96, 7, 12, 0, 0, }, /* 604 */
- { 96, 15, 12, 0, 0, }, /* 605 */
- { 30, 7, 12, 0, 0, }, /* 606 */
- { 30, 12, 3, 0, 0, }, /* 607 */
- { 30, 15, 12, 0, 0, }, /* 608 */
- { 30, 21, 12, 0, 0, }, /* 609 */
- { 87, 7, 12, 0, 0, }, /* 610 */
- { 87, 15, 12, 0, 0, }, /* 611 */
- { 87, 21, 12, 0, 0, }, /* 612 */
- { 116, 7, 12, 0, 0, }, /* 613 */
- { 116, 15, 12, 0, 0, }, /* 614 */
- { 111, 7, 12, 0, 0, }, /* 615 */
- { 111, 26, 12, 0, 0, }, /* 616 */
- { 111, 12, 3, 0, 0, }, /* 617 */
- { 111, 15, 12, 0, 0, }, /* 618 */
- { 111, 21, 12, 0, 0, }, /* 619 */
- { 77, 7, 12, 0, 0, }, /* 620 */
- { 77, 21, 12, 0, 0, }, /* 621 */
- { 82, 7, 12, 0, 0, }, /* 622 */
- { 82, 15, 12, 0, 0, }, /* 623 */
- { 81, 7, 12, 0, 0, }, /* 624 */
- { 81, 15, 12, 0, 0, }, /* 625 */
- { 120, 7, 12, 0, 0, }, /* 626 */
- { 120, 21, 12, 0, 0, }, /* 627 */
- { 120, 15, 12, 0, 0, }, /* 628 */
- { 88, 7, 12, 0, 0, }, /* 629 */
- { 129, 9, 12, 0, 64, }, /* 630 */
- { 129, 5, 12, 0, -64, }, /* 631 */
- { 129, 15, 12, 0, 0, }, /* 632 */
- { 0, 15, 12, 0, 0, }, /* 633 */
- { 93, 10, 5, 0, 0, }, /* 634 */
- { 93, 12, 3, 0, 0, }, /* 635 */
- { 93, 7, 12, 0, 0, }, /* 636 */
- { 93, 21, 12, 0, 0, }, /* 637 */
- { 93, 15, 12, 0, 0, }, /* 638 */
- { 93, 13, 12, 0, 0, }, /* 639 */
- { 84, 12, 3, 0, 0, }, /* 640 */
- { 84, 10, 5, 0, 0, }, /* 641 */
- { 84, 7, 12, 0, 0, }, /* 642 */
- { 84, 21, 12, 0, 0, }, /* 643 */
- { 84, 1, 2, 0, 0, }, /* 644 */
- { 100, 7, 12, 0, 0, }, /* 645 */
- { 100, 13, 12, 0, 0, }, /* 646 */
- { 95, 12, 3, 0, 0, }, /* 647 */
- { 95, 7, 12, 0, 0, }, /* 648 */
- { 95, 10, 5, 0, 0, }, /* 649 */
- { 95, 13, 12, 0, 0, }, /* 650 */
- { 95, 21, 12, 0, 0, }, /* 651 */
- { 110, 7, 12, 0, 0, }, /* 652 */
- { 110, 12, 3, 0, 0, }, /* 653 */
- { 110, 21, 12, 0, 0, }, /* 654 */
- { 99, 12, 3, 0, 0, }, /* 655 */
- { 99, 10, 5, 0, 0, }, /* 656 */
- { 99, 7, 12, 0, 0, }, /* 657 */
- { 99, 21, 12, 0, 0, }, /* 658 */
- { 99, 13, 12, 0, 0, }, /* 659 */
- { 47, 15, 12, 0, 0, }, /* 660 */
- { 107, 7, 12, 0, 0, }, /* 661 */
- { 107, 10, 5, 0, 0, }, /* 662 */
- { 107, 12, 3, 0, 0, }, /* 663 */
- { 107, 21, 12, 0, 0, }, /* 664 */
- { 128, 7, 12, 0, 0, }, /* 665 */
- { 128, 21, 12, 0, 0, }, /* 666 */
- { 108, 7, 12, 0, 0, }, /* 667 */
- { 108, 12, 3, 0, 0, }, /* 668 */
- { 108, 10, 5, 0, 0, }, /* 669 */
- { 108, 13, 12, 0, 0, }, /* 670 */
- { 106, 12, 3, 0, 0, }, /* 671 */
- { 106, 10, 5, 0, 0, }, /* 672 */
- { 106, 7, 12, 0, 0, }, /* 673 */
- { 106, 10, 3, 0, 0, }, /* 674 */
- { 123, 7, 12, 0, 0, }, /* 675 */
- { 123, 10, 3, 0, 0, }, /* 676 */
- { 123, 10, 5, 0, 0, }, /* 677 */
- { 123, 12, 3, 0, 0, }, /* 678 */
- { 123, 21, 12, 0, 0, }, /* 679 */
- { 123, 13, 12, 0, 0, }, /* 680 */
- { 122, 7, 12, 0, 0, }, /* 681 */
- { 122, 10, 3, 0, 0, }, /* 682 */
- { 122, 10, 5, 0, 0, }, /* 683 */
- { 122, 12, 3, 0, 0, }, /* 684 */
- { 122, 21, 12, 0, 0, }, /* 685 */
- { 113, 7, 12, 0, 0, }, /* 686 */
- { 113, 10, 5, 0, 0, }, /* 687 */
- { 113, 12, 3, 0, 0, }, /* 688 */
- { 113, 21, 12, 0, 0, }, /* 689 */
- { 113, 13, 12, 0, 0, }, /* 690 */
- { 101, 7, 12, 0, 0, }, /* 691 */
- { 101, 12, 3, 0, 0, }, /* 692 */
- { 101, 10, 5, 0, 0, }, /* 693 */
- { 101, 13, 12, 0, 0, }, /* 694 */
- { 125, 7, 12, 0, 0, }, /* 695 */
- { 125, 12, 3, 0, 0, }, /* 696 */
- { 125, 10, 5, 0, 0, }, /* 697 */
- { 125, 13, 12, 0, 0, }, /* 698 */
- { 125, 15, 12, 0, 0, }, /* 699 */
- { 125, 21, 12, 0, 0, }, /* 700 */
- { 125, 26, 12, 0, 0, }, /* 701 */
- { 124, 9, 12, 0, 32, }, /* 702 */
- { 124, 5, 12, 0, -32, }, /* 703 */
- { 124, 13, 12, 0, 0, }, /* 704 */
- { 124, 15, 12, 0, 0, }, /* 705 */
- { 124, 7, 12, 0, 0, }, /* 706 */
- { 121, 7, 12, 0, 0, }, /* 707 */
- { 62, 7, 12, 0, 0, }, /* 708 */
- { 62, 14, 12, 0, 0, }, /* 709 */
- { 62, 21, 12, 0, 0, }, /* 710 */
- { 79, 7, 12, 0, 0, }, /* 711 */
- { 126, 7, 12, 0, 0, }, /* 712 */
- { 114, 7, 12, 0, 0, }, /* 713 */
- { 114, 13, 12, 0, 0, }, /* 714 */
- { 114, 21, 12, 0, 0, }, /* 715 */
- { 102, 7, 12, 0, 0, }, /* 716 */
- { 102, 12, 3, 0, 0, }, /* 717 */
- { 102, 21, 12, 0, 0, }, /* 718 */
- { 118, 7, 12, 0, 0, }, /* 719 */
- { 118, 12, 3, 0, 0, }, /* 720 */
- { 118, 21, 12, 0, 0, }, /* 721 */
- { 118, 26, 12, 0, 0, }, /* 722 */
- { 118, 6, 12, 0, 0, }, /* 723 */
- { 118, 13, 12, 0, 0, }, /* 724 */
- { 118, 15, 12, 0, 0, }, /* 725 */
- { 98, 7, 12, 0, 0, }, /* 726 */
- { 98, 10, 5, 0, 0, }, /* 727 */
- { 98, 12, 3, 0, 0, }, /* 728 */
- { 98, 6, 12, 0, 0, }, /* 729 */
- { 104, 7, 12, 0, 0, }, /* 730 */
- { 104, 26, 12, 0, 0, }, /* 731 */
- { 104, 12, 3, 0, 0, }, /* 732 */
- { 104, 21, 12, 0, 0, }, /* 733 */
- { 9, 10, 3, 0, 0, }, /* 734 */
- { 19, 12, 3, 0, 0, }, /* 735 */
- { 130, 26, 12, 0, 0, }, /* 736 */
- { 130, 12, 3, 0, 0, }, /* 737 */
- { 130, 21, 12, 0, 0, }, /* 738 */
- { 112, 7, 12, 0, 0, }, /* 739 */
- { 112, 15, 12, 0, 0, }, /* 740 */
- { 112, 12, 3, 0, 0, }, /* 741 */
- { 9, 26, 11, 0, 0, }, /* 742 */
- { 26, 26, 12, 0, 0, }, /* 743 */
+ { 12, 9, 12, 63, 32, }, /* 173 */
+ { 12, 9, 12, 67, 32, }, /* 174 */
+ { 12, 9, 12, 71, 32, }, /* 175 */
+ { 12, 9, 12, 75, 32, }, /* 176 */
+ { 12, 9, 12, 79, 32, }, /* 177 */
+ { 12, 9, 12, 84, 32, }, /* 178 */
+ { 12, 5, 12, 0, -32, }, /* 179 */
+ { 12, 5, 12, 63, -32, }, /* 180 */
+ { 12, 5, 12, 67, -32, }, /* 181 */
+ { 12, 5, 12, 71, -32, }, /* 182 */
+ { 12, 5, 12, 75, -32, }, /* 183 */
+ { 12, 5, 12, 79, -32, }, /* 184 */
+ { 12, 5, 12, 84, -32, }, /* 185 */
+ { 12, 5, 12, 0, -80, }, /* 186 */
+ { 12, 9, 12, 0, 1, }, /* 187 */
+ { 12, 5, 12, 0, -1, }, /* 188 */
+ { 12, 9, 12, 88, 1, }, /* 189 */
+ { 12, 5, 12, 88, -1, }, /* 190 */
+ { 12, 26, 12, 0, 0, }, /* 191 */
+ { 12, 12, 3, 0, 0, }, /* 192 */
+ { 12, 11, 3, 0, 0, }, /* 193 */
+ { 12, 9, 12, 0, 15, }, /* 194 */
+ { 12, 5, 12, 0, -15, }, /* 195 */
+ { 1, 9, 12, 0, 48, }, /* 196 */
+ { 1, 6, 12, 0, 0, }, /* 197 */
+ { 1, 21, 12, 0, 0, }, /* 198 */
+ { 1, 5, 12, 0, -48, }, /* 199 */
+ { 1, 5, 12, 0, 0, }, /* 200 */
+ { 1, 17, 12, 0, 0, }, /* 201 */
+ { 1, 26, 12, 0, 0, }, /* 202 */
+ { 1, 23, 12, 0, 0, }, /* 203 */
+ { 25, 12, 3, 0, 0, }, /* 204 */
+ { 25, 17, 12, 0, 0, }, /* 205 */
+ { 25, 21, 12, 0, 0, }, /* 206 */
+ { 25, 7, 12, 0, 0, }, /* 207 */
+ { 0, 1, 4, 0, 0, }, /* 208 */
+ { 9, 1, 4, 0, 0, }, /* 209 */
+ { 0, 25, 12, 0, 0, }, /* 210 */
+ { 0, 21, 12, 0, 0, }, /* 211 */
+ { 0, 23, 12, 0, 0, }, /* 212 */
+ { 0, 26, 12, 0, 0, }, /* 213 */
+ { 0, 12, 3, 0, 0, }, /* 214 */
+ { 0, 1, 2, 0, 0, }, /* 215 */
+ { 0, 7, 12, 0, 0, }, /* 216 */
+ { 0, 13, 12, 0, 0, }, /* 217 */
+ { 0, 6, 12, 0, 0, }, /* 218 */
+ { 49, 21, 12, 0, 0, }, /* 219 */
+ { 49, 1, 4, 0, 0, }, /* 220 */
+ { 49, 7, 12, 0, 0, }, /* 221 */
+ { 49, 12, 3, 0, 0, }, /* 222 */
+ { 55, 7, 12, 0, 0, }, /* 223 */
+ { 55, 12, 3, 0, 0, }, /* 224 */
+ { 63, 13, 12, 0, 0, }, /* 225 */
+ { 63, 7, 12, 0, 0, }, /* 226 */
+ { 63, 12, 3, 0, 0, }, /* 227 */
+ { 63, 6, 12, 0, 0, }, /* 228 */
+ { 63, 26, 12, 0, 0, }, /* 229 */
+ { 63, 21, 12, 0, 0, }, /* 230 */
+ { 89, 7, 12, 0, 0, }, /* 231 */
+ { 89, 12, 3, 0, 0, }, /* 232 */
+ { 89, 6, 12, 0, 0, }, /* 233 */
+ { 89, 21, 12, 0, 0, }, /* 234 */
+ { 94, 7, 12, 0, 0, }, /* 235 */
+ { 94, 12, 3, 0, 0, }, /* 236 */
+ { 94, 21, 12, 0, 0, }, /* 237 */
+ { 14, 12, 3, 0, 0, }, /* 238 */
+ { 14, 10, 5, 0, 0, }, /* 239 */
+ { 14, 7, 12, 0, 0, }, /* 240 */
+ { 14, 13, 12, 0, 0, }, /* 241 */
+ { 14, 21, 12, 0, 0, }, /* 242 */
+ { 14, 6, 12, 0, 0, }, /* 243 */
+ { 2, 7, 12, 0, 0, }, /* 244 */
+ { 2, 12, 3, 0, 0, }, /* 245 */
+ { 2, 10, 5, 0, 0, }, /* 246 */
+ { 2, 10, 3, 0, 0, }, /* 247 */
+ { 2, 13, 12, 0, 0, }, /* 248 */
+ { 2, 23, 12, 0, 0, }, /* 249 */
+ { 2, 15, 12, 0, 0, }, /* 250 */
+ { 2, 26, 12, 0, 0, }, /* 251 */
+ { 2, 21, 12, 0, 0, }, /* 252 */
+ { 21, 12, 3, 0, 0, }, /* 253 */
+ { 21, 10, 5, 0, 0, }, /* 254 */
+ { 21, 7, 12, 0, 0, }, /* 255 */
+ { 21, 13, 12, 0, 0, }, /* 256 */
+ { 20, 12, 3, 0, 0, }, /* 257 */
+ { 20, 10, 5, 0, 0, }, /* 258 */
+ { 20, 7, 12, 0, 0, }, /* 259 */
+ { 20, 13, 12, 0, 0, }, /* 260 */
+ { 20, 21, 12, 0, 0, }, /* 261 */
+ { 20, 23, 12, 0, 0, }, /* 262 */
+ { 43, 12, 3, 0, 0, }, /* 263 */
+ { 43, 10, 5, 0, 0, }, /* 264 */
+ { 43, 7, 12, 0, 0, }, /* 265 */
+ { 43, 10, 3, 0, 0, }, /* 266 */
+ { 43, 13, 12, 0, 0, }, /* 267 */
+ { 43, 26, 12, 0, 0, }, /* 268 */
+ { 43, 15, 12, 0, 0, }, /* 269 */
+ { 53, 12, 3, 0, 0, }, /* 270 */
+ { 53, 7, 12, 0, 0, }, /* 271 */
+ { 53, 10, 3, 0, 0, }, /* 272 */
+ { 53, 10, 5, 0, 0, }, /* 273 */
+ { 53, 13, 12, 0, 0, }, /* 274 */
+ { 53, 15, 12, 0, 0, }, /* 275 */
+ { 53, 26, 12, 0, 0, }, /* 276 */
+ { 53, 23, 12, 0, 0, }, /* 277 */
+ { 54, 12, 3, 0, 0, }, /* 278 */
+ { 54, 10, 5, 0, 0, }, /* 279 */
+ { 54, 7, 12, 0, 0, }, /* 280 */
+ { 54, 13, 12, 0, 0, }, /* 281 */
+ { 54, 15, 12, 0, 0, }, /* 282 */
+ { 54, 26, 12, 0, 0, }, /* 283 */
+ { 28, 7, 12, 0, 0, }, /* 284 */
+ { 28, 12, 3, 0, 0, }, /* 285 */
+ { 28, 10, 5, 0, 0, }, /* 286 */
+ { 28, 10, 3, 0, 0, }, /* 287 */
+ { 28, 13, 12, 0, 0, }, /* 288 */
+ { 36, 12, 3, 0, 0, }, /* 289 */
+ { 36, 10, 5, 0, 0, }, /* 290 */
+ { 36, 7, 12, 0, 0, }, /* 291 */
+ { 36, 10, 3, 0, 0, }, /* 292 */
+ { 36, 7, 4, 0, 0, }, /* 293 */
+ { 36, 26, 12, 0, 0, }, /* 294 */
+ { 36, 15, 12, 0, 0, }, /* 295 */
+ { 36, 13, 12, 0, 0, }, /* 296 */
+ { 47, 10, 5, 0, 0, }, /* 297 */
+ { 47, 7, 12, 0, 0, }, /* 298 */
+ { 47, 12, 3, 0, 0, }, /* 299 */
+ { 47, 10, 3, 0, 0, }, /* 300 */
+ { 47, 13, 12, 0, 0, }, /* 301 */
+ { 47, 21, 12, 0, 0, }, /* 302 */
+ { 56, 7, 12, 0, 0, }, /* 303 */
+ { 56, 12, 3, 0, 0, }, /* 304 */
+ { 56, 7, 5, 0, 0, }, /* 305 */
+ { 56, 6, 12, 0, 0, }, /* 306 */
+ { 56, 21, 12, 0, 0, }, /* 307 */
+ { 56, 13, 12, 0, 0, }, /* 308 */
+ { 32, 7, 12, 0, 0, }, /* 309 */
+ { 32, 12, 3, 0, 0, }, /* 310 */
+ { 32, 7, 5, 0, 0, }, /* 311 */
+ { 32, 6, 12, 0, 0, }, /* 312 */
+ { 32, 13, 12, 0, 0, }, /* 313 */
+ { 57, 7, 12, 0, 0, }, /* 314 */
+ { 57, 26, 12, 0, 0, }, /* 315 */
+ { 57, 21, 12, 0, 0, }, /* 316 */
+ { 57, 12, 3, 0, 0, }, /* 317 */
+ { 57, 13, 12, 0, 0, }, /* 318 */
+ { 57, 15, 12, 0, 0, }, /* 319 */
+ { 57, 22, 12, 0, 0, }, /* 320 */
+ { 57, 18, 12, 0, 0, }, /* 321 */
+ { 57, 10, 5, 0, 0, }, /* 322 */
+ { 38, 7, 12, 0, 0, }, /* 323 */
+ { 38, 10, 12, 0, 0, }, /* 324 */
+ { 38, 12, 3, 0, 0, }, /* 325 */
+ { 38, 10, 5, 0, 0, }, /* 326 */
+ { 38, 13, 12, 0, 0, }, /* 327 */
+ { 38, 21, 12, 0, 0, }, /* 328 */
+ { 38, 26, 12, 0, 0, }, /* 329 */
+ { 16, 9, 12, 0, 7264, }, /* 330 */
+ { 16, 7, 12, 0, 0, }, /* 331 */
+ { 16, 6, 12, 0, 0, }, /* 332 */
+ { 23, 7, 6, 0, 0, }, /* 333 */
+ { 23, 7, 7, 0, 0, }, /* 334 */
+ { 23, 7, 8, 0, 0, }, /* 335 */
+ { 15, 7, 12, 0, 0, }, /* 336 */
+ { 15, 12, 3, 0, 0, }, /* 337 */
+ { 15, 21, 12, 0, 0, }, /* 338 */
+ { 15, 15, 12, 0, 0, }, /* 339 */
+ { 15, 26, 12, 0, 0, }, /* 340 */
+ { 8, 9, 12, 0, 38864, }, /* 341 */
+ { 8, 9, 12, 0, 8, }, /* 342 */
+ { 8, 5, 12, 0, -8, }, /* 343 */
+ { 7, 17, 12, 0, 0, }, /* 344 */
+ { 7, 7, 12, 0, 0, }, /* 345 */
+ { 7, 21, 12, 0, 0, }, /* 346 */
+ { 40, 29, 12, 0, 0, }, /* 347 */
+ { 40, 7, 12, 0, 0, }, /* 348 */
+ { 40, 22, 12, 0, 0, }, /* 349 */
+ { 40, 18, 12, 0, 0, }, /* 350 */
+ { 45, 7, 12, 0, 0, }, /* 351 */
+ { 45, 14, 12, 0, 0, }, /* 352 */
+ { 50, 7, 12, 0, 0, }, /* 353 */
+ { 50, 12, 3, 0, 0, }, /* 354 */
+ { 24, 7, 12, 0, 0, }, /* 355 */
+ { 24, 12, 3, 0, 0, }, /* 356 */
+ { 6, 7, 12, 0, 0, }, /* 357 */
+ { 6, 12, 3, 0, 0, }, /* 358 */
+ { 51, 7, 12, 0, 0, }, /* 359 */
+ { 51, 12, 3, 0, 0, }, /* 360 */
+ { 31, 7, 12, 0, 0, }, /* 361 */
+ { 31, 12, 3, 0, 0, }, /* 362 */
+ { 31, 10, 5, 0, 0, }, /* 363 */
+ { 31, 21, 12, 0, 0, }, /* 364 */
+ { 31, 6, 12, 0, 0, }, /* 365 */
+ { 31, 23, 12, 0, 0, }, /* 366 */
+ { 31, 13, 12, 0, 0, }, /* 367 */
+ { 31, 15, 12, 0, 0, }, /* 368 */
+ { 37, 21, 12, 0, 0, }, /* 369 */
+ { 37, 17, 12, 0, 0, }, /* 370 */
+ { 37, 12, 3, 0, 0, }, /* 371 */
+ { 37, 1, 2, 0, 0, }, /* 372 */
+ { 37, 13, 12, 0, 0, }, /* 373 */
+ { 37, 7, 12, 0, 0, }, /* 374 */
+ { 37, 6, 12, 0, 0, }, /* 375 */
+ { 34, 7, 12, 0, 0, }, /* 376 */
+ { 34, 12, 3, 0, 0, }, /* 377 */
+ { 34, 10, 5, 0, 0, }, /* 378 */
+ { 34, 26, 12, 0, 0, }, /* 379 */
+ { 34, 21, 12, 0, 0, }, /* 380 */
+ { 34, 13, 12, 0, 0, }, /* 381 */
+ { 52, 7, 12, 0, 0, }, /* 382 */
+ { 39, 7, 12, 0, 0, }, /* 383 */
+ { 39, 13, 12, 0, 0, }, /* 384 */
+ { 39, 15, 12, 0, 0, }, /* 385 */
+ { 39, 26, 12, 0, 0, }, /* 386 */
+ { 31, 26, 12, 0, 0, }, /* 387 */
+ { 5, 7, 12, 0, 0, }, /* 388 */
+ { 5, 12, 3, 0, 0, }, /* 389 */
+ { 5, 10, 5, 0, 0, }, /* 390 */
+ { 5, 21, 12, 0, 0, }, /* 391 */
+ { 90, 7, 12, 0, 0, }, /* 392 */
+ { 90, 10, 5, 0, 0, }, /* 393 */
+ { 90, 12, 3, 0, 0, }, /* 394 */
+ { 90, 10, 12, 0, 0, }, /* 395 */
+ { 90, 13, 12, 0, 0, }, /* 396 */
+ { 90, 21, 12, 0, 0, }, /* 397 */
+ { 90, 6, 12, 0, 0, }, /* 398 */
+ { 27, 11, 3, 0, 0, }, /* 399 */
+ { 61, 12, 3, 0, 0, }, /* 400 */
+ { 61, 10, 5, 0, 0, }, /* 401 */
+ { 61, 7, 12, 0, 0, }, /* 402 */
+ { 61, 13, 12, 0, 0, }, /* 403 */
+ { 61, 21, 12, 0, 0, }, /* 404 */
+ { 61, 26, 12, 0, 0, }, /* 405 */
+ { 75, 12, 3, 0, 0, }, /* 406 */
+ { 75, 10, 5, 0, 0, }, /* 407 */
+ { 75, 7, 12, 0, 0, }, /* 408 */
+ { 75, 13, 12, 0, 0, }, /* 409 */
+ { 92, 7, 12, 0, 0, }, /* 410 */
+ { 92, 12, 3, 0, 0, }, /* 411 */
+ { 92, 10, 5, 0, 0, }, /* 412 */
+ { 92, 21, 12, 0, 0, }, /* 413 */
+ { 69, 7, 12, 0, 0, }, /* 414 */
+ { 69, 10, 5, 0, 0, }, /* 415 */
+ { 69, 12, 3, 0, 0, }, /* 416 */
+ { 69, 21, 12, 0, 0, }, /* 417 */
+ { 69, 13, 12, 0, 0, }, /* 418 */
+ { 72, 13, 12, 0, 0, }, /* 419 */
+ { 72, 7, 12, 0, 0, }, /* 420 */
+ { 72, 6, 12, 0, 0, }, /* 421 */
+ { 72, 21, 12, 0, 0, }, /* 422 */
+ { 12, 5, 12, 63, -6222, }, /* 423 */
+ { 12, 5, 12, 67, -6221, }, /* 424 */
+ { 12, 5, 12, 71, -6212, }, /* 425 */
+ { 12, 5, 12, 75, -6210, }, /* 426 */
+ { 12, 5, 12, 79, -6210, }, /* 427 */
+ { 12, 5, 12, 79, -6211, }, /* 428 */
+ { 12, 5, 12, 84, -6204, }, /* 429 */
+ { 12, 5, 12, 88, -6180, }, /* 430 */
+ { 12, 5, 12, 108, 35267, }, /* 431 */
+ { 75, 21, 12, 0, 0, }, /* 432 */
+ { 9, 10, 5, 0, 0, }, /* 433 */
+ { 9, 7, 12, 0, 0, }, /* 434 */
+ { 12, 5, 12, 0, 0, }, /* 435 */
+ { 12, 6, 12, 0, 0, }, /* 436 */
+ { 33, 5, 12, 0, 35332, }, /* 437 */
+ { 33, 5, 12, 0, 3814, }, /* 438 */
+ { 33, 9, 12, 92, 1, }, /* 439 */
+ { 33, 5, 12, 92, -1, }, /* 440 */
+ { 33, 5, 12, 92, -58, }, /* 441 */
+ { 33, 9, 12, 0, -7615, }, /* 442 */
+ { 19, 5, 12, 0, 8, }, /* 443 */
+ { 19, 9, 12, 0, -8, }, /* 444 */
+ { 19, 5, 12, 0, 74, }, /* 445 */
+ { 19, 5, 12, 0, 86, }, /* 446 */
+ { 19, 5, 12, 0, 100, }, /* 447 */
+ { 19, 5, 12, 0, 128, }, /* 448 */
+ { 19, 5, 12, 0, 112, }, /* 449 */
+ { 19, 5, 12, 0, 126, }, /* 450 */
+ { 19, 8, 12, 0, -8, }, /* 451 */
+ { 19, 5, 12, 0, 9, }, /* 452 */
+ { 19, 9, 12, 0, -74, }, /* 453 */
+ { 19, 8, 12, 0, -9, }, /* 454 */
+ { 19, 5, 12, 21, -7173, }, /* 455 */
+ { 19, 9, 12, 0, -86, }, /* 456 */
+ { 19, 9, 12, 0, -100, }, /* 457 */
+ { 19, 9, 12, 0, -112, }, /* 458 */
+ { 19, 9, 12, 0, -128, }, /* 459 */
+ { 19, 9, 12, 0, -126, }, /* 460 */
+ { 27, 1, 3, 0, 0, }, /* 461 */
+ { 27, 1, 16, 0, 0, }, /* 462 */
+ { 9, 27, 2, 0, 0, }, /* 463 */
+ { 9, 28, 2, 0, 0, }, /* 464 */
+ { 9, 2, 2, 0, 0, }, /* 465 */
+ { 9, 9, 12, 0, 0, }, /* 466 */
+ { 9, 5, 12, 0, 0, }, /* 467 */
+ { 19, 9, 12, 96, -7517, }, /* 468 */
+ { 33, 9, 12, 100, -8383, }, /* 469 */
+ { 33, 9, 12, 104, -8262, }, /* 470 */
+ { 33, 9, 12, 0, 28, }, /* 471 */
+ { 33, 5, 12, 0, -28, }, /* 472 */
+ { 33, 14, 12, 0, 16, }, /* 473 */
+ { 33, 14, 12, 0, -16, }, /* 474 */
+ { 33, 14, 12, 0, 0, }, /* 475 */
+ { 9, 26, 12, 0, 26, }, /* 476 */
+ { 9, 26, 12, 0, -26, }, /* 477 */
+ { 9, 26, 13, 0, 0, }, /* 478 */
+ { 9, 26, 17, 0, 0, }, /* 479 */
+ { 4, 26, 12, 0, 0, }, /* 480 */
+ { 17, 9, 12, 0, 48, }, /* 481 */
+ { 17, 5, 12, 0, -48, }, /* 482 */
+ { 33, 9, 12, 0, -10743, }, /* 483 */
+ { 33, 9, 12, 0, -3814, }, /* 484 */
+ { 33, 9, 12, 0, -10727, }, /* 485 */
+ { 33, 5, 12, 0, -10795, }, /* 486 */
+ { 33, 5, 12, 0, -10792, }, /* 487 */
+ { 33, 9, 12, 0, -10780, }, /* 488 */
+ { 33, 9, 12, 0, -10749, }, /* 489 */
+ { 33, 9, 12, 0, -10783, }, /* 490 */
+ { 33, 9, 12, 0, -10782, }, /* 491 */
+ { 33, 9, 12, 0, -10815, }, /* 492 */
+ { 10, 5, 12, 0, 0, }, /* 493 */
+ { 10, 26, 12, 0, 0, }, /* 494 */
+ { 10, 12, 3, 0, 0, }, /* 495 */
+ { 10, 21, 12, 0, 0, }, /* 496 */
+ { 10, 15, 12, 0, 0, }, /* 497 */
+ { 16, 5, 12, 0, -7264, }, /* 498 */
+ { 58, 7, 12, 0, 0, }, /* 499 */
+ { 58, 6, 12, 0, 0, }, /* 500 */
+ { 58, 21, 12, 0, 0, }, /* 501 */
+ { 58, 12, 3, 0, 0, }, /* 502 */
+ { 22, 26, 12, 0, 0, }, /* 503 */
+ { 22, 6, 12, 0, 0, }, /* 504 */
+ { 22, 14, 12, 0, 0, }, /* 505 */
+ { 23, 10, 3, 0, 0, }, /* 506 */
+ { 26, 7, 12, 0, 0, }, /* 507 */
+ { 26, 6, 12, 0, 0, }, /* 508 */
+ { 29, 7, 12, 0, 0, }, /* 509 */
+ { 29, 6, 12, 0, 0, }, /* 510 */
+ { 3, 7, 12, 0, 0, }, /* 511 */
+ { 23, 7, 12, 0, 0, }, /* 512 */
+ { 23, 26, 12, 0, 0, }, /* 513 */
+ { 29, 26, 12, 0, 0, }, /* 514 */
+ { 22, 7, 12, 0, 0, }, /* 515 */
+ { 60, 7, 12, 0, 0, }, /* 516 */
+ { 60, 6, 12, 0, 0, }, /* 517 */
+ { 60, 26, 12, 0, 0, }, /* 518 */
+ { 85, 7, 12, 0, 0, }, /* 519 */
+ { 85, 6, 12, 0, 0, }, /* 520 */
+ { 85, 21, 12, 0, 0, }, /* 521 */
+ { 76, 7, 12, 0, 0, }, /* 522 */
+ { 76, 6, 12, 0, 0, }, /* 523 */
+ { 76, 21, 12, 0, 0, }, /* 524 */
+ { 76, 13, 12, 0, 0, }, /* 525 */
+ { 12, 9, 12, 108, 1, }, /* 526 */
+ { 12, 5, 12, 108, -35267, }, /* 527 */
+ { 12, 7, 12, 0, 0, }, /* 528 */
+ { 12, 21, 12, 0, 0, }, /* 529 */
+ { 78, 7, 12, 0, 0, }, /* 530 */
+ { 78, 14, 12, 0, 0, }, /* 531 */
+ { 78, 12, 3, 0, 0, }, /* 532 */
+ { 78, 21, 12, 0, 0, }, /* 533 */
+ { 33, 9, 12, 0, -35332, }, /* 534 */
+ { 33, 9, 12, 0, -42280, }, /* 535 */
+ { 33, 9, 12, 0, -42308, }, /* 536 */
+ { 33, 9, 12, 0, -42319, }, /* 537 */
+ { 33, 9, 12, 0, -42315, }, /* 538 */
+ { 33, 9, 12, 0, -42305, }, /* 539 */
+ { 33, 9, 12, 0, -42258, }, /* 540 */
+ { 33, 9, 12, 0, -42282, }, /* 541 */
+ { 33, 9, 12, 0, -42261, }, /* 542 */
+ { 33, 9, 12, 0, 928, }, /* 543 */
+ { 48, 7, 12, 0, 0, }, /* 544 */
+ { 48, 12, 3, 0, 0, }, /* 545 */
+ { 48, 10, 5, 0, 0, }, /* 546 */
+ { 48, 26, 12, 0, 0, }, /* 547 */
+ { 64, 7, 12, 0, 0, }, /* 548 */
+ { 64, 21, 12, 0, 0, }, /* 549 */
+ { 74, 10, 5, 0, 0, }, /* 550 */
+ { 74, 7, 12, 0, 0, }, /* 551 */
+ { 74, 12, 3, 0, 0, }, /* 552 */
+ { 74, 21, 12, 0, 0, }, /* 553 */
+ { 74, 13, 12, 0, 0, }, /* 554 */
+ { 68, 13, 12, 0, 0, }, /* 555 */
+ { 68, 7, 12, 0, 0, }, /* 556 */
+ { 68, 12, 3, 0, 0, }, /* 557 */
+ { 68, 21, 12, 0, 0, }, /* 558 */
+ { 73, 7, 12, 0, 0, }, /* 559 */
+ { 73, 12, 3, 0, 0, }, /* 560 */
+ { 73, 10, 5, 0, 0, }, /* 561 */
+ { 73, 21, 12, 0, 0, }, /* 562 */
+ { 83, 12, 3, 0, 0, }, /* 563 */
+ { 83, 10, 5, 0, 0, }, /* 564 */
+ { 83, 7, 12, 0, 0, }, /* 565 */
+ { 83, 21, 12, 0, 0, }, /* 566 */
+ { 83, 13, 12, 0, 0, }, /* 567 */
+ { 38, 6, 12, 0, 0, }, /* 568 */
+ { 67, 7, 12, 0, 0, }, /* 569 */
+ { 67, 12, 3, 0, 0, }, /* 570 */
+ { 67, 10, 5, 0, 0, }, /* 571 */
+ { 67, 13, 12, 0, 0, }, /* 572 */
+ { 67, 21, 12, 0, 0, }, /* 573 */
+ { 91, 7, 12, 0, 0, }, /* 574 */
+ { 91, 12, 3, 0, 0, }, /* 575 */
+ { 91, 6, 12, 0, 0, }, /* 576 */
+ { 91, 21, 12, 0, 0, }, /* 577 */
+ { 86, 7, 12, 0, 0, }, /* 578 */
+ { 86, 10, 5, 0, 0, }, /* 579 */
+ { 86, 12, 3, 0, 0, }, /* 580 */
+ { 86, 21, 12, 0, 0, }, /* 581 */
+ { 86, 6, 12, 0, 0, }, /* 582 */
+ { 33, 5, 12, 0, -928, }, /* 583 */
+ { 8, 5, 12, 0, -38864, }, /* 584 */
+ { 86, 13, 12, 0, 0, }, /* 585 */
+ { 23, 7, 9, 0, 0, }, /* 586 */
+ { 23, 7, 10, 0, 0, }, /* 587 */
+ { 9, 4, 2, 0, 0, }, /* 588 */
+ { 9, 3, 12, 0, 0, }, /* 589 */
+ { 25, 25, 12, 0, 0, }, /* 590 */
+ { 0, 24, 12, 0, 0, }, /* 591 */
+ { 9, 6, 3, 0, 0, }, /* 592 */
+ { 35, 7, 12, 0, 0, }, /* 593 */
+ { 19, 14, 12, 0, 0, }, /* 594 */
+ { 19, 15, 12, 0, 0, }, /* 595 */
+ { 19, 26, 12, 0, 0, }, /* 596 */
+ { 70, 7, 12, 0, 0, }, /* 597 */
+ { 66, 7, 12, 0, 0, }, /* 598 */
+ { 41, 7, 12, 0, 0, }, /* 599 */
+ { 41, 15, 12, 0, 0, }, /* 600 */
+ { 18, 7, 12, 0, 0, }, /* 601 */
+ { 18, 14, 12, 0, 0, }, /* 602 */
+ { 117, 7, 12, 0, 0, }, /* 603 */
+ { 117, 12, 3, 0, 0, }, /* 604 */
+ { 59, 7, 12, 0, 0, }, /* 605 */
+ { 59, 21, 12, 0, 0, }, /* 606 */
+ { 42, 7, 12, 0, 0, }, /* 607 */
+ { 42, 21, 12, 0, 0, }, /* 608 */
+ { 42, 14, 12, 0, 0, }, /* 609 */
+ { 13, 9, 12, 0, 40, }, /* 610 */
+ { 13, 5, 12, 0, -40, }, /* 611 */
+ { 46, 7, 12, 0, 0, }, /* 612 */
+ { 44, 7, 12, 0, 0, }, /* 613 */
+ { 44, 13, 12, 0, 0, }, /* 614 */
+ { 135, 9, 12, 0, 40, }, /* 615 */
+ { 135, 5, 12, 0, -40, }, /* 616 */
+ { 105, 7, 12, 0, 0, }, /* 617 */
+ { 103, 7, 12, 0, 0, }, /* 618 */
+ { 103, 21, 12, 0, 0, }, /* 619 */
+ { 109, 7, 12, 0, 0, }, /* 620 */
+ { 11, 7, 12, 0, 0, }, /* 621 */
+ { 80, 7, 12, 0, 0, }, /* 622 */
+ { 80, 21, 12, 0, 0, }, /* 623 */
+ { 80, 15, 12, 0, 0, }, /* 624 */
+ { 119, 7, 12, 0, 0, }, /* 625 */
+ { 119, 26, 12, 0, 0, }, /* 626 */
+ { 119, 15, 12, 0, 0, }, /* 627 */
+ { 115, 7, 12, 0, 0, }, /* 628 */
+ { 115, 15, 12, 0, 0, }, /* 629 */
+ { 127, 7, 12, 0, 0, }, /* 630 */
+ { 127, 15, 12, 0, 0, }, /* 631 */
+ { 65, 7, 12, 0, 0, }, /* 632 */
+ { 65, 15, 12, 0, 0, }, /* 633 */
+ { 65, 21, 12, 0, 0, }, /* 634 */
+ { 71, 7, 12, 0, 0, }, /* 635 */
+ { 71, 21, 12, 0, 0, }, /* 636 */
+ { 97, 7, 12, 0, 0, }, /* 637 */
+ { 96, 7, 12, 0, 0, }, /* 638 */
+ { 96, 15, 12, 0, 0, }, /* 639 */
+ { 30, 7, 12, 0, 0, }, /* 640 */
+ { 30, 12, 3, 0, 0, }, /* 641 */
+ { 30, 15, 12, 0, 0, }, /* 642 */
+ { 30, 21, 12, 0, 0, }, /* 643 */
+ { 87, 7, 12, 0, 0, }, /* 644 */
+ { 87, 15, 12, 0, 0, }, /* 645 */
+ { 87, 21, 12, 0, 0, }, /* 646 */
+ { 116, 7, 12, 0, 0, }, /* 647 */
+ { 116, 15, 12, 0, 0, }, /* 648 */
+ { 111, 7, 12, 0, 0, }, /* 649 */
+ { 111, 26, 12, 0, 0, }, /* 650 */
+ { 111, 12, 3, 0, 0, }, /* 651 */
+ { 111, 15, 12, 0, 0, }, /* 652 */
+ { 111, 21, 12, 0, 0, }, /* 653 */
+ { 77, 7, 12, 0, 0, }, /* 654 */
+ { 77, 21, 12, 0, 0, }, /* 655 */
+ { 82, 7, 12, 0, 0, }, /* 656 */
+ { 82, 15, 12, 0, 0, }, /* 657 */
+ { 81, 7, 12, 0, 0, }, /* 658 */
+ { 81, 15, 12, 0, 0, }, /* 659 */
+ { 120, 7, 12, 0, 0, }, /* 660 */
+ { 120, 21, 12, 0, 0, }, /* 661 */
+ { 120, 15, 12, 0, 0, }, /* 662 */
+ { 88, 7, 12, 0, 0, }, /* 663 */
+ { 129, 9, 12, 0, 64, }, /* 664 */
+ { 129, 5, 12, 0, -64, }, /* 665 */
+ { 129, 15, 12, 0, 0, }, /* 666 */
+ { 0, 15, 12, 0, 0, }, /* 667 */
+ { 93, 10, 5, 0, 0, }, /* 668 */
+ { 93, 12, 3, 0, 0, }, /* 669 */
+ { 93, 7, 12, 0, 0, }, /* 670 */
+ { 93, 21, 12, 0, 0, }, /* 671 */
+ { 93, 15, 12, 0, 0, }, /* 672 */
+ { 93, 13, 12, 0, 0, }, /* 673 */
+ { 84, 12, 3, 0, 0, }, /* 674 */
+ { 84, 10, 5, 0, 0, }, /* 675 */
+ { 84, 7, 12, 0, 0, }, /* 676 */
+ { 84, 21, 12, 0, 0, }, /* 677 */
+ { 84, 1, 4, 0, 0, }, /* 678 */
+ { 100, 7, 12, 0, 0, }, /* 679 */
+ { 100, 13, 12, 0, 0, }, /* 680 */
+ { 95, 12, 3, 0, 0, }, /* 681 */
+ { 95, 7, 12, 0, 0, }, /* 682 */
+ { 95, 10, 5, 0, 0, }, /* 683 */
+ { 95, 13, 12, 0, 0, }, /* 684 */
+ { 95, 21, 12, 0, 0, }, /* 685 */
+ { 110, 7, 12, 0, 0, }, /* 686 */
+ { 110, 12, 3, 0, 0, }, /* 687 */
+ { 110, 21, 12, 0, 0, }, /* 688 */
+ { 99, 12, 3, 0, 0, }, /* 689 */
+ { 99, 10, 5, 0, 0, }, /* 690 */
+ { 99, 7, 12, 0, 0, }, /* 691 */
+ { 99, 7, 4, 0, 0, }, /* 692 */
+ { 99, 21, 12, 0, 0, }, /* 693 */
+ { 99, 13, 12, 0, 0, }, /* 694 */
+ { 47, 15, 12, 0, 0, }, /* 695 */
+ { 107, 7, 12, 0, 0, }, /* 696 */
+ { 107, 10, 5, 0, 0, }, /* 697 */
+ { 107, 12, 3, 0, 0, }, /* 698 */
+ { 107, 21, 12, 0, 0, }, /* 699 */
+ { 128, 7, 12, 0, 0, }, /* 700 */
+ { 128, 21, 12, 0, 0, }, /* 701 */
+ { 108, 7, 12, 0, 0, }, /* 702 */
+ { 108, 12, 3, 0, 0, }, /* 703 */
+ { 108, 10, 5, 0, 0, }, /* 704 */
+ { 108, 13, 12, 0, 0, }, /* 705 */
+ { 106, 12, 3, 0, 0, }, /* 706 */
+ { 106, 10, 5, 0, 0, }, /* 707 */
+ { 106, 7, 12, 0, 0, }, /* 708 */
+ { 106, 10, 3, 0, 0, }, /* 709 */
+ { 134, 7, 12, 0, 0, }, /* 710 */
+ { 134, 10, 5, 0, 0, }, /* 711 */
+ { 134, 12, 3, 0, 0, }, /* 712 */
+ { 134, 21, 12, 0, 0, }, /* 713 */
+ { 134, 13, 12, 0, 0, }, /* 714 */
+ { 123, 7, 12, 0, 0, }, /* 715 */
+ { 123, 10, 3, 0, 0, }, /* 716 */
+ { 123, 10, 5, 0, 0, }, /* 717 */
+ { 123, 12, 3, 0, 0, }, /* 718 */
+ { 123, 21, 12, 0, 0, }, /* 719 */
+ { 123, 13, 12, 0, 0, }, /* 720 */
+ { 122, 7, 12, 0, 0, }, /* 721 */
+ { 122, 10, 3, 0, 0, }, /* 722 */
+ { 122, 10, 5, 0, 0, }, /* 723 */
+ { 122, 12, 3, 0, 0, }, /* 724 */
+ { 122, 21, 12, 0, 0, }, /* 725 */
+ { 113, 7, 12, 0, 0, }, /* 726 */
+ { 113, 10, 5, 0, 0, }, /* 727 */
+ { 113, 12, 3, 0, 0, }, /* 728 */
+ { 113, 21, 12, 0, 0, }, /* 729 */
+ { 113, 13, 12, 0, 0, }, /* 730 */
+ { 101, 7, 12, 0, 0, }, /* 731 */
+ { 101, 12, 3, 0, 0, }, /* 732 */
+ { 101, 10, 5, 0, 0, }, /* 733 */
+ { 101, 13, 12, 0, 0, }, /* 734 */
+ { 125, 7, 12, 0, 0, }, /* 735 */
+ { 125, 12, 3, 0, 0, }, /* 736 */
+ { 125, 10, 5, 0, 0, }, /* 737 */
+ { 125, 13, 12, 0, 0, }, /* 738 */
+ { 125, 15, 12, 0, 0, }, /* 739 */
+ { 125, 21, 12, 0, 0, }, /* 740 */
+ { 125, 26, 12, 0, 0, }, /* 741 */
+ { 124, 9, 12, 0, 32, }, /* 742 */
+ { 124, 5, 12, 0, -32, }, /* 743 */
+ { 124, 13, 12, 0, 0, }, /* 744 */
+ { 124, 15, 12, 0, 0, }, /* 745 */
+ { 124, 7, 12, 0, 0, }, /* 746 */
+ { 140, 7, 12, 0, 0, }, /* 747 */
+ { 140, 12, 3, 0, 0, }, /* 748 */
+ { 140, 10, 5, 0, 0, }, /* 749 */
+ { 140, 7, 4, 0, 0, }, /* 750 */
+ { 140, 21, 12, 0, 0, }, /* 751 */
+ { 139, 7, 12, 0, 0, }, /* 752 */
+ { 139, 12, 3, 0, 0, }, /* 753 */
+ { 139, 10, 5, 0, 0, }, /* 754 */
+ { 139, 7, 4, 0, 0, }, /* 755 */
+ { 139, 21, 12, 0, 0, }, /* 756 */
+ { 121, 7, 12, 0, 0, }, /* 757 */
+ { 132, 7, 12, 0, 0, }, /* 758 */
+ { 132, 10, 5, 0, 0, }, /* 759 */
+ { 132, 12, 3, 0, 0, }, /* 760 */
+ { 132, 21, 12, 0, 0, }, /* 761 */
+ { 132, 13, 12, 0, 0, }, /* 762 */
+ { 132, 15, 12, 0, 0, }, /* 763 */
+ { 133, 21, 12, 0, 0, }, /* 764 */
+ { 133, 7, 12, 0, 0, }, /* 765 */
+ { 133, 12, 3, 0, 0, }, /* 766 */
+ { 133, 10, 5, 0, 0, }, /* 767 */
+ { 137, 7, 12, 0, 0, }, /* 768 */
+ { 137, 12, 3, 0, 0, }, /* 769 */
+ { 137, 7, 4, 0, 0, }, /* 770 */
+ { 137, 13, 12, 0, 0, }, /* 771 */
+ { 62, 7, 12, 0, 0, }, /* 772 */
+ { 62, 14, 12, 0, 0, }, /* 773 */
+ { 62, 21, 12, 0, 0, }, /* 774 */
+ { 79, 7, 12, 0, 0, }, /* 775 */
+ { 126, 7, 12, 0, 0, }, /* 776 */
+ { 114, 7, 12, 0, 0, }, /* 777 */
+ { 114, 13, 12, 0, 0, }, /* 778 */
+ { 114, 21, 12, 0, 0, }, /* 779 */
+ { 102, 7, 12, 0, 0, }, /* 780 */
+ { 102, 12, 3, 0, 0, }, /* 781 */
+ { 102, 21, 12, 0, 0, }, /* 782 */
+ { 118, 7, 12, 0, 0, }, /* 783 */
+ { 118, 12, 3, 0, 0, }, /* 784 */
+ { 118, 21, 12, 0, 0, }, /* 785 */
+ { 118, 26, 12, 0, 0, }, /* 786 */
+ { 118, 6, 12, 0, 0, }, /* 787 */
+ { 118, 13, 12, 0, 0, }, /* 788 */
+ { 118, 15, 12, 0, 0, }, /* 789 */
+ { 98, 7, 12, 0, 0, }, /* 790 */
+ { 98, 10, 5, 0, 0, }, /* 791 */
+ { 98, 12, 3, 0, 0, }, /* 792 */
+ { 98, 6, 12, 0, 0, }, /* 793 */
+ { 136, 6, 12, 0, 0, }, /* 794 */
+ { 138, 6, 12, 0, 0, }, /* 795 */
+ { 136, 7, 12, 0, 0, }, /* 796 */
+ { 138, 7, 12, 0, 0, }, /* 797 */
+ { 104, 7, 12, 0, 0, }, /* 798 */
+ { 104, 26, 12, 0, 0, }, /* 799 */
+ { 104, 12, 3, 0, 0, }, /* 800 */
+ { 104, 21, 12, 0, 0, }, /* 801 */
+ { 9, 10, 3, 0, 0, }, /* 802 */
+ { 19, 12, 3, 0, 0, }, /* 803 */
+ { 130, 26, 12, 0, 0, }, /* 804 */
+ { 130, 12, 3, 0, 0, }, /* 805 */
+ { 130, 21, 12, 0, 0, }, /* 806 */
+ { 17, 12, 3, 0, 0, }, /* 807 */
+ { 112, 7, 12, 0, 0, }, /* 808 */
+ { 112, 15, 12, 0, 0, }, /* 809 */
+ { 112, 12, 3, 0, 0, }, /* 810 */
+ { 131, 9, 12, 0, 34, }, /* 811 */
+ { 131, 5, 12, 0, -34, }, /* 812 */
+ { 131, 12, 3, 0, 0, }, /* 813 */
+ { 131, 13, 12, 0, 0, }, /* 814 */
+ { 131, 21, 12, 0, 0, }, /* 815 */
+ { 9, 26, 11, 0, 0, }, /* 816 */
+ { 26, 26, 12, 0, 0, }, /* 817 */
+ { 9, 24, 14, 0, 0, }, /* 818 */
+ { 9, 26, 15, 0, 0, }, /* 819 */
+ { 9, 1, 3, 0, 0, }, /* 820 */
};
const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */
@@ -834,549 +933,549 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */
- 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 71, 74, 75, /* U+2000 */
- 76, 76, 66, 77, 66, 66, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, /* U+2800 */
- 88, 89, 90, 91, 92, 93, 94, 71, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+4000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 95, 95, 95, 95, /* U+4800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+9000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 97, /* U+9800 */
- 98, 99, 99, 99, 99, 99, 99, 99, 99,100,101,101,102,103,104,105, /* U+A000 */
-106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,114, /* U+A800 */
-115,116,117,118,119,120,114,115,116,117,118,119,120,114,115,116, /* U+B000 */
-117,118,119,120,114,115,116,117,118,119,120,114,115,116,117,118, /* U+B800 */
-119,120,114,115,116,117,118,119,120,114,115,116,117,118,119,120, /* U+C000 */
-114,115,116,117,118,119,120,114,115,116,117,118,119,120,114,115, /* U+C800 */
-116,117,118,119,120,114,115,116,117,118,119,120,114,115,116,121, /* U+D000 */
-122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, /* U+D800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F000 */
-123,123, 95, 95,124,125,126,127,128,128,129,130,131,132,133,134, /* U+F800 */
-135,136,137,138,139,140,141,142,143,144,145,139,146,146,147,139, /* U+10000 */
-148,149,150,151,152,153,154,155,156,157,139,139,158,139,139,139, /* U+10800 */
-159,160,161,162,163,164,165,139,139,166,139,167,168,169,170,139, /* U+11000 */
-139,171,139,139,139,172,139,139,139,139,139,139,139,139,139,139, /* U+11800 */
-173,173,173,173,173,173,173,174,175,173,176,139,139,139,139,139, /* U+12000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+12800 */
-177,177,177,177,177,177,177,177,178,139,139,139,139,139,139,139, /* U+13000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+13800 */
-139,139,139,139,139,139,139,139,179,179,179,179,180,139,139,139, /* U+14000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+14800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+15000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+15800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+16000 */
-181,181,181,181,182,183,184,185,139,139,139,139,139,139,186,187, /* U+16800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+17000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+17800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+18000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+18800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+19000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+19800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1A800 */
-188,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1B000 */
-139,139,139,139,139,139,139,139,189,190,139,139,139,139,139,139, /* U+1B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1C800 */
- 71,191,192,193,194,139,195,139,196,197,198,199,200,201,202,203, /* U+1D000 */
-204,204,204,204,205,206,139,139,139,139,139,139,139,139,139,139, /* U+1D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1E000 */
-207,208,139,139,139,139,139,139,139,139,139,139,209,210,139,139, /* U+1E800 */
-211,212,213,214,215,139, 71,216, 71, 71,217,218, 71,219,220,221, /* U+1F000 */
-222,223,224,225,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1F800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,226, 95, 95, /* U+2A000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2A800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,227, 95, /* U+2B000 */
-228, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2B800 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2C000 */
- 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,229,139,139, /* U+2C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2F000 */
- 95, 95, 95, 95,230,139,139,139,139,139,139,139,139,139,139,139, /* U+2F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+30000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+30800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+31000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+31800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+32000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+32800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+33000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+33800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+34000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+34800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+35000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+35800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+36000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+36800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+37000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+37800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+38000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+38800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+39000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+39800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+40000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+40800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+41000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+41800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+42000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+42800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+43000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+43800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+44000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+44800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+45000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+45800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+46000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+46800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+47000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+47800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+48000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+48800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+49000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+49800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+50000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+50800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+51000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+51800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+52000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+52800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+53000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+53800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+54000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+54800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+55000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+55800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+56000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+56800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+57000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+57800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+58000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+58800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+59000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+59800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+60000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+60800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+61000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+61800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+62000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+62800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+63000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+63800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+64000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+64800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+65000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+65800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+66000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+66800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+67000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+67800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+68000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+68800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+69000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+69800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+70000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+70800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+71000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+71800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+72000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+72800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+73000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+73800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+74000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+74800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+75000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+75800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+76000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+76800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+77000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+77800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+78000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+78800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+79000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+79800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+80000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+80800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+81000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+81800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+82000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+82800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+83000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+83800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+84000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+84800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+85000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+85800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+86000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+86800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+87000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+87800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+88000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+88800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+89000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+89800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+90000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+90800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+91000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+91800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+92000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+92800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+93000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+93800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+94000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+94800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+95000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+95800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+96000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+96800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+97000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+97800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+98000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+98800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+99000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+99800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9A000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9A800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9B000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9B800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9C000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9C800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9D000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9D800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9E000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9E800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9F000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9F800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A0000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A0800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A1000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A1800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A2000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A2800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A3000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A3800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A4000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A4800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A5000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A5800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A6000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A6800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A7000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A7800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A8000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A8800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A9000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A9800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AA000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AA800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AB000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AB800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AC000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AC800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AD000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AD800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AE000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AE800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AF000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AF800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B0000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B0800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B1000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B1800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B2000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B2800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B3000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B3800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B4000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B4800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B5000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B5800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B6000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B6800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B7000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B7800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B8000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B8800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B9000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B9800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BA000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BA800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BB000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BB800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BC000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BC800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BD000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BD800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BE000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BE800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BF000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BF800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C0000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C0800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C1000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C1800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C2000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C2800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C3000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C3800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C4000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C4800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C5000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C5800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C6000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C6800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C7000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C7800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C8000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C8800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C9000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C9800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CA000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CA800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CB000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CB800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CC000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CC800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CD000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CD800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CE000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CE800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CF000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CF800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D0000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D0800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D1000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D1800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D2000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D2800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D3000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D3800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D4000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D4800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D5000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D5800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D6000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D6800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D7000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D7800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D8000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D8800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D9000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D9800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DA000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DA800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DB000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DB800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DC000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DC800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DD000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DD800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DE000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DE800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DF000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DF800 */
-231,232,233,234,232,232,232,232,232,232,232,232,232,232,232,232, /* U+E0000 */
-232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, /* U+E0800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E1000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E1800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E2000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E2800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E3000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E3800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E4000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E4800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E5000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E5800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E6000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E6800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E7000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E7800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E8000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E8800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E9000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E9800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EA000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EA800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EB000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EB800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EC000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EC800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+ED000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+ED800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EE000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EE800 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EF000 */
-139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EF800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FF000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,235, /* U+FF800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E800 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10F000 */
-123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,235, /* U+10F800 */
+ 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, /* U+2000 */
+ 77, 77, 66, 78, 66, 66, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, /* U+2800 */
+ 89, 90, 91, 92, 93, 94, 95, 71, 96, 96, 96, 96, 96, 96, 96, 96, /* U+3000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+3800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+4000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 96, 96, 96, 96, /* U+4800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+5000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+5800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+6000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+6800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+7000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+7800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+8000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+8800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+9000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 98, /* U+9800 */
+ 99,100,100,100,100,100,100,100,100,101,102,102,103,104,105,106, /* U+A000 */
+107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,115, /* U+A800 */
+116,117,118,119,120,121,115,116,117,118,119,120,121,115,116,117, /* U+B000 */
+118,119,120,121,115,116,117,118,119,120,121,115,116,117,118,119, /* U+B800 */
+120,121,115,116,117,118,119,120,121,115,116,117,118,119,120,121, /* U+C000 */
+115,116,117,118,119,120,121,115,116,117,118,119,120,121,115,116, /* U+C800 */
+117,118,119,120,121,115,116,117,118,119,120,121,115,116,117,122, /* U+D000 */
+123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+D800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+E000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+E800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F000 */
+124,124, 96, 96,125,126,127,128,129,129,130,131,132,133,134,135, /* U+F800 */
+136,137,138,139,140,141,142,143,144,145,146,140,147,147,148,140, /* U+10000 */
+149,150,151,152,153,154,155,156,157,158,140,140,159,140,140,140, /* U+10800 */
+160,161,162,163,164,165,166,140,167,168,140,169,170,171,172,140, /* U+11000 */
+140,173,140,140,174,175,140,140,176,177,178,140,140,140,140,140, /* U+11800 */
+179,179,179,179,179,179,179,180,181,179,182,140,140,140,140,140, /* U+12000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+12800 */
+183,183,183,183,183,183,183,183,184,140,140,140,140,140,140,140, /* U+13000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+13800 */
+140,140,140,140,140,140,140,140,185,185,185,185,186,140,140,140, /* U+14000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+14800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+15000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+15800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+16000 */
+187,187,187,187,188,189,190,191,140,140,140,140,140,140,192,193, /* U+16800 */
+194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194, /* U+17000 */
+194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194, /* U+17800 */
+194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,195, /* U+18000 */
+194,194,194,194,194,196,140,140,140,140,140,140,140,140,140,140, /* U+18800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+19000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+19800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1A800 */
+197,198,199,200,200,201,140,140,140,140,140,140,140,140,140,140, /* U+1B000 */
+140,140,140,140,140,140,140,140,202,203,140,140,140,140,140,140, /* U+1B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1C800 */
+ 71,204,205,206,207,140,208,140,209,210,211,212,213,214,215,216, /* U+1D000 */
+217,217,217,217,218,219,140,140,140,140,140,140,140,140,140,140, /* U+1D800 */
+220,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1E000 */
+221,222,223,140,140,140,140,140,140,140,140,140,224,225,140,140, /* U+1E800 */
+226,227,228,229,230,140,231,232,233,234,235,236,237,238,239,240, /* U+1F000 */
+241,242,243,244,140,140,140,140,140,140,140,140,140,140,140,140, /* U+1F800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+20000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+20800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+21000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+21800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+22000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+22800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+23000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+23800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+24000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+24800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+25000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+25800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+26000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+26800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+27000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+27800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+28000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+28800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+29000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+29800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,245, 96, 96, /* U+2A000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2A800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,246, 96, /* U+2B000 */
+247, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2B800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2C000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,248, 96, 96, /* U+2C800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2D000 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2D800 */
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* U+2E000 */
+ 96, 96, 96, 96, 96, 96, 96,249,140,140,140,140,140,140,140,140, /* U+2E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+2F000 */
+ 96, 96, 96, 96,250,140,140,140,140,140,140,140,140,140,140,140, /* U+2F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+30000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+30800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+31000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+31800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+32000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+32800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+33000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+33800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+34000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+34800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+35000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+35800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+36000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+36800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+37000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+37800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+38000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+38800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+39000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+39800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+3F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+40000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+40800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+41000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+41800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+42000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+42800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+43000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+43800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+44000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+44800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+45000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+45800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+46000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+46800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+47000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+47800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+48000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+48800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+49000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+49800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+4F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+50000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+50800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+51000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+51800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+52000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+52800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+53000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+53800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+54000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+54800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+55000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+55800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+56000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+56800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+57000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+57800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+58000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+58800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+59000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+59800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+5F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+60000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+60800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+61000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+61800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+62000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+62800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+63000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+63800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+64000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+64800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+65000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+65800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+66000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+66800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+67000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+67800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+68000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+68800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+69000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+69800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+6F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+70000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+70800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+71000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+71800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+72000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+72800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+73000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+73800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+74000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+74800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+75000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+75800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+76000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+76800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+77000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+77800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+78000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+78800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+79000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+79800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+7F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+80000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+80800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+81000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+81800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+82000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+82800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+83000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+83800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+84000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+84800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+85000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+85800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+86000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+86800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+87000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+87800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+88000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+88800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+89000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+89800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+8F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+90000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+90800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+91000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+91800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+92000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+92800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+93000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+93800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+94000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+94800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+95000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+95800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+96000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+96800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+97000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+97800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+98000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+98800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+99000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+99800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9A000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9A800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9B000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9B800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9C000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9C800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9D000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9D800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9E000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9E800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9F000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+9F800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A0000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A0800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A1000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A1800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A2000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A2800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A3000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A3800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A4000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A4800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A5000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A5800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A6000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A6800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A7000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A7800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A8000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A8800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A9000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+A9800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AA000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AA800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AB000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AB800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AC000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AC800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AD000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AD800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AE000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AE800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AF000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+AF800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B0000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B0800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B1000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B1800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B2000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B2800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B3000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B3800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B4000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B4800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B5000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B5800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B6000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B6800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B7000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B7800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B8000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B8800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B9000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+B9800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BA000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BA800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BB000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BB800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BC000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BC800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BD000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BD800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BE000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BE800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BF000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+BF800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C0000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C0800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C1000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C1800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C2000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C2800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C3000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C3800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C4000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C4800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C5000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C5800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C6000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C6800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C7000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C7800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C8000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C8800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C9000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+C9800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CA000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CA800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CB000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CB800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CC000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CC800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CD000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CD800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CE000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CE800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CF000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+CF800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D0000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D0800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D1000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D1800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D2000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D2800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D3000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D3800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D4000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D4800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D5000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D5800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D6000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D6800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D7000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D7800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D8000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D8800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D9000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+D9800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DA000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DA800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DB000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DB800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DC000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DC800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DD000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DD800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DE000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DE800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DF000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+DF800 */
+251,252,253,254,252,252,252,252,252,252,252,252,252,252,252,252, /* U+E0000 */
+252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252, /* U+E0800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E1000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E1800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E2000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E2800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E3000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E3800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E4000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E4800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E5000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E5800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E6000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E6800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E7000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E7800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E8000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E8800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E9000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+E9800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EA000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EA800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EB000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EB800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EC000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EC800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+ED000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+ED800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EE000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EE800 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EF000 */
+140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, /* U+EF800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F0000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F0800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F1000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F1800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F2000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F2800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F3000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F3800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F4000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F4800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F5000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F5800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F6000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F6800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F7000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F7800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F8000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F8800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F9000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+F9800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FA000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FA800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FB000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FB800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FC000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FC800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FD000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FD800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FE000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FE800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+FF000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,255, /* U+FF800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+100000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+100800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+101000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+101800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+102000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+102800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+103000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+103800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+104000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+104800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+105000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+105800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+106000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+106800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+107000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+107800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+108000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+108800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+109000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+109800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10A000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10A800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10B000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10B800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10C000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10C800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10D000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10D800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10E000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10E800 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+10F000 */
+124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,255, /* U+10F800 */
};
-const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
+const uint16_t PRIV(ucd_stage2)[] = { /* 65536 bytes, block = 128 */
/* block 0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1424,7 +1523,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
30, 31, 30, 31, 33, 33, 33, 33, 33, 33, 71, 30, 31, 72, 73, 74,
74, 30, 31, 75, 76, 77, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
78, 79, 80, 81, 82, 33, 83, 83, 33, 84, 33, 85, 86, 33, 33, 33,
- 83, 87, 33, 88, 33, 89, 90, 33, 91, 92, 33, 93, 94, 33, 33, 92,
+ 83, 87, 33, 88, 33, 89, 90, 33, 91, 92, 90, 93, 94, 33, 33, 92,
33, 95, 96, 33, 33, 97, 33, 33, 33, 33, 33, 33, 33, 98, 33, 33,
/* block 5 */
@@ -1459,493 +1558,493 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
/* block 8 */
171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,
-172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,
-172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,
-173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,
-173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,
-174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
+172,172,173,172,174,172,172,172,172,172,172,172,172,172,175,172,
+172,176,177,172,172,172,172,172,172,172,178,172,172,172,172,172,
+179,179,180,179,181,179,179,179,179,179,179,179,179,179,182,179,
+179,183,184,179,179,179,179,179,179,179,185,179,179,179,179,179,
+186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,
+187,188,189,190,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
/* block 9 */
-175,176,177,178,178,110,110,178,179,179,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-180,175,176,175,176,175,176,175,176,175,176,175,176,175,176,181,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
+187,188,191,192,192,110,110,192,193,193,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+194,187,188,187,188,187,188,187,188,187,188,187,188,187,188,195,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
/* block 10 */
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-115,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,
-182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,
-182,182,182,182,182,182,182,115,115,183,184,184,184,184,184,184,
-115,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,
-185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+115,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,
+196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,
+196,196,196,196,196,196,196,115,115,197,198,198,198,198,198,198,
+115,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,
+199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,
/* block 11 */
-185,185,185,185,185,185,185,186,115, 4,187,115,115,188,188,189,
-115,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,
-190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,
-190,190,190,190,190,190,190,190,190,190,190,190,190,190,191,190,
-192,190,190,192,190,190,192,190,115,115,115,115,115,115,115,115,
-193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,
-193,193,193,193,193,193,193,193,193,193,193,115,115,115,115,115,
-193,193,193,192,192,115,115,115,115,115,115,115,115,115,115,115,
+199,199,199,199,199,199,199,200,115, 4,201,115,115,202,202,203,
+115,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,
+204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,
+204,204,204,204,204,204,204,204,204,204,204,204,204,204,205,204,
+206,204,204,206,204,204,206,204,115,115,115,115,115,115,115,115,
+207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,
+207,207,207,207,207,207,207,207,207,207,207,115,115,115,115,115,
+207,207,207,206,206,115,115,115,115,115,115,115,115,115,115,115,
/* block 12 */
-194,194,194,194,194, 22,195,195,195,196,196,197, 4,196,198,198,
-199,199,199,199,199,199,199,199,199,199,199, 4, 22,115,196, 4,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-108,200,200,200,200,200,200,200,200,200,200,110,110,110,110,110,
-110,110,110,110,110,110,199,199,199,199,199,199,199,199,199,199,
-201,201,201,201,201,201,201,201,201,201,196,196,196,196,200,200,
-110,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+208,208,208,208,208,209,210,210,210,211,211,212, 4,211,213,213,
+214,214,214,214,214,214,214,214,214,214,214, 4,215,115,211, 4,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+108,216,216,216,216,216,216,216,216,216,216,110,110,110,110,110,
+110,110,110,110,110,110,214,214,214,214,214,214,214,214,214,214,
+217,217,217,217,217,217,217,217,217,217,211,211,211,211,216,216,
+110,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 13 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,196,200,199,199,199,199,199,199,199, 22,198,199,
-199,199,199,199,199,202,202,199,199,198,199,199,199,199,200,200,
-201,201,201,201,201,201,201,201,201,201,200,200,200,198,198,200,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,211,216,214,214,214,214,214,214,214,209,213,214,
+214,214,214,214,214,218,218,214,214,213,214,214,214,214,216,216,
+217,217,217,217,217,217,217,217,217,217,216,216,216,213,213,216,
/* block 14 */
-203,203,203,203,203,203,203,203,203,203,203,203,203,203,115,204,
-205,206,205,205,205,205,205,205,205,205,205,205,205,205,205,205,
-205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,
-206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,
-206,206,206,206,206,206,206,206,206,206,206,115,115,205,205,205,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+219,219,219,219,219,219,219,219,219,219,219,219,219,219,115,220,
+221,222,221,221,221,221,221,221,221,221,221,221,221,221,221,221,
+221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,
+222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,
+222,222,222,222,222,222,222,222,222,222,222,115,115,221,221,221,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 15 */
-207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,
-207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,
-207,207,207,207,207,207,208,208,208,208,208,208,208,208,208,208,
-208,207,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-209,209,209,209,209,209,209,209,209,209,210,210,210,210,210,210,
-210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
-210,210,210,210,210,210,210,210,210,210,210,211,211,211,211,211,
-211,211,211,211,212,212,213,214,214,214,212,115,115,115,115,115,
+223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+223,223,223,223,223,223,224,224,224,224,224,224,224,224,224,224,
+224,223,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+225,225,225,225,225,225,225,225,225,225,226,226,226,226,226,226,
+226,226,226,226,226,226,226,226,226,226,226,226,226,226,226,226,
+226,226,226,226,226,226,226,226,226,226,226,227,227,227,227,227,
+227,227,227,227,228,228,229,230,230,230,228,115,115,115,115,115,
/* block 16 */
-215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,
-215,215,215,215,215,215,216,216,216,216,217,216,216,216,216,216,
-216,216,216,216,217,216,216,216,217,216,216,216,216,216,115,115,
-218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,115,
-219,219,219,219,219,219,219,219,219,219,219,219,219,219,219,219,
-219,219,219,219,219,219,219,219,219,220,220,220,115,115,221,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,
+231,231,231,231,231,231,232,232,232,232,233,232,232,232,232,232,
+232,232,232,232,233,232,232,232,233,232,232,232,232,232,115,115,
+234,234,234,234,234,234,234,234,234,234,234,234,234,234,234,115,
+235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,
+235,235,235,235,235,235,235,235,235,236,236,236,115,115,237,115,
+221,221,221,221,221,221,221,221,221,221,221,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 17 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,115,115,115,115,115,115,115,115,115,115,115,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,115,216,216,216,216,216,216,216,216,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,199,199,199,199,199,199,199,199,199,199,199,199,199,
-199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,
+115,115,115,115,214,214,214,214,214,214,214,214,214,214,214,214,
+214,214,209,214,214,214,214,214,214,214,214,214,214,214,214,214,
+214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,
/* block 18 */
-222,222,222,223,224,224,224,224,224,224,224,224,224,224,224,224,
-224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,
-224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,
-224,224,224,224,224,224,224,224,224,224,222,223,222,224,223,223,
-223,222,222,222,222,222,222,222,222,223,223,223,223,222,223,223,
-224,110,110,222,222,222,222,222,224,224,224,224,224,224,224,224,
-224,224,222,222, 4, 4,225,225,225,225,225,225,225,225,225,225,
-226,227,224,224,224,224,224,224,224,224,224,224,224,224,224,224,
+238,238,238,239,240,240,240,240,240,240,240,240,240,240,240,240,
+240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+240,240,240,240,240,240,240,240,240,240,238,239,238,240,239,239,
+239,238,238,238,238,238,238,238,238,239,239,239,239,238,239,239,
+240,110,110,238,238,238,238,238,240,240,240,240,240,240,240,240,
+240,240,238,238, 4, 4,241,241,241,241,241,241,241,241,241,241,
+242,243,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
/* block 19 */
-228,229,230,230,115,228,228,228,228,228,228,228,228,115,115,228,
-228,115,115,228,228,228,228,228,228,228,228,228,228,228,228,228,
-228,228,228,228,228,228,228,228,228,115,228,228,228,228,228,228,
-228,115,228,115,115,115,228,228,228,228,115,115,229,228,231,230,
-230,229,229,229,229,115,115,230,230,115,115,230,230,229,228,115,
-115,115,115,115,115,115,115,231,115,115,115,115,228,228,115,228,
-228,228,229,229,115,115,232,232,232,232,232,232,232,232,232,232,
-228,228,233,233,234,234,234,234,234,234,235,233,115,115,115,115,
+244,245,246,246,115,244,244,244,244,244,244,244,244,115,115,244,
+244,115,115,244,244,244,244,244,244,244,244,244,244,244,244,244,
+244,244,244,244,244,244,244,244,244,115,244,244,244,244,244,244,
+244,115,244,115,115,115,244,244,244,244,115,115,245,244,247,246,
+246,245,245,245,245,115,115,246,246,115,115,246,246,245,244,115,
+115,115,115,115,115,115,115,247,115,115,115,115,244,244,115,244,
+244,244,245,245,115,115,248,248,248,248,248,248,248,248,248,248,
+244,244,249,249,250,250,250,250,250,250,251,249,244,252,115,115,
/* block 20 */
-115,236,236,237,115,238,238,238,238,238,238,115,115,115,115,238,
-238,115,115,238,238,238,238,238,238,238,238,238,238,238,238,238,
-238,238,238,238,238,238,238,238,238,115,238,238,238,238,238,238,
-238,115,238,238,115,238,238,115,238,238,115,115,236,115,237,237,
-237,236,236,115,115,115,115,236,236,115,115,236,236,236,115,115,
-115,236,115,115,115,115,115,115,115,238,238,238,238,115,238,115,
-115,115,115,115,115,115,239,239,239,239,239,239,239,239,239,239,
-236,236,238,238,238,236,115,115,115,115,115,115,115,115,115,115,
+115,253,253,254,115,255,255,255,255,255,255,115,115,115,115,255,
+255,115,115,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,115,255,255,255,255,255,255,
+255,115,255,255,115,255,255,115,255,255,115,115,253,115,254,254,
+254,253,253,115,115,115,115,253,253,115,115,253,253,253,115,115,
+115,253,115,115,115,115,115,115,115,255,255,255,255,115,255,115,
+115,115,115,115,115,115,256,256,256,256,256,256,256,256,256,256,
+253,253,255,255,255,253,115,115,115,115,115,115,115,115,115,115,
/* block 21 */
-115,240,240,241,115,242,242,242,242,242,242,242,242,242,115,242,
-242,242,115,242,242,242,242,242,242,242,242,242,242,242,242,242,
-242,242,242,242,242,242,242,242,242,115,242,242,242,242,242,242,
-242,115,242,242,115,242,242,242,242,242,115,115,240,242,241,241,
-241,240,240,240,240,240,115,240,240,241,115,241,241,240,115,115,
-242,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-242,242,240,240,115,115,243,243,243,243,243,243,243,243,243,243,
-244,245,115,115,115,115,115,115,115,242,115,115,115,115,115,115,
+115,257,257,258,115,259,259,259,259,259,259,259,259,259,115,259,
+259,259,115,259,259,259,259,259,259,259,259,259,259,259,259,259,
+259,259,259,259,259,259,259,259,259,115,259,259,259,259,259,259,
+259,115,259,259,115,259,259,259,259,259,115,115,257,259,258,258,
+258,257,257,257,257,257,115,257,257,258,115,258,258,257,115,115,
+259,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+259,259,257,257,115,115,260,260,260,260,260,260,260,260,260,260,
+261,262,115,115,115,115,115,115,115,259,257,257,257,257,257,257,
/* block 22 */
-115,246,247,247,115,248,248,248,248,248,248,248,248,115,115,248,
-248,115,115,248,248,248,248,248,248,248,248,248,248,248,248,248,
-248,248,248,248,248,248,248,248,248,115,248,248,248,248,248,248,
-248,115,248,248,115,248,248,248,248,248,115,115,246,248,249,246,
-247,246,246,246,246,115,115,247,247,115,115,247,247,246,115,115,
-115,115,115,115,115,115,246,249,115,115,115,115,248,248,115,248,
-248,248,246,246,115,115,250,250,250,250,250,250,250,250,250,250,
-251,248,252,252,252,252,252,252,115,115,115,115,115,115,115,115,
+115,263,264,264,115,265,265,265,265,265,265,265,265,115,115,265,
+265,115,115,265,265,265,265,265,265,265,265,265,265,265,265,265,
+265,265,265,265,265,265,265,265,265,115,265,265,265,265,265,265,
+265,115,265,265,115,265,265,265,265,265,115,115,263,265,266,263,
+264,263,263,263,263,115,115,264,264,115,115,264,264,263,115,115,
+115,115,115,115,115,115,263,266,115,115,115,115,265,265,115,265,
+265,265,263,263,115,115,267,267,267,267,267,267,267,267,267,267,
+268,265,269,269,269,269,269,269,115,115,115,115,115,115,115,115,
/* block 23 */
-115,115,253,254,115,254,254,254,254,254,254,115,115,115,254,254,
-254,115,254,254,254,254,115,115,115,254,254,115,254,115,254,254,
-115,115,115,254,254,115,115,115,254,254,254,115,115,115,254,254,
-254,254,254,254,254,254,254,254,254,254,115,115,115,115,255,256,
-253,256,256,115,115,115,256,256,256,115,256,256,256,253,115,115,
-254,115,115,115,115,115,115,255,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,257,257,257,257,257,257,257,257,257,257,
-258,258,258,259,259,259,259,259,259,260,259,115,115,115,115,115,
+115,115,270,271,115,271,271,271,271,271,271,115,115,115,271,271,
+271,115,271,271,271,271,115,115,115,271,271,115,271,115,271,271,
+115,115,115,271,271,115,115,115,271,271,271,115,115,115,271,271,
+271,271,271,271,271,271,271,271,271,271,115,115,115,115,272,273,
+270,273,273,115,115,115,273,273,273,115,273,273,273,270,115,115,
+271,115,115,115,115,115,115,272,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,274,274,274,274,274,274,274,274,274,274,
+275,275,275,276,276,276,276,276,276,277,276,115,115,115,115,115,
/* block 24 */
-261,262,262,262,115,263,263,263,263,263,263,263,263,115,263,263,
-263,115,263,263,263,263,263,263,263,263,263,263,263,263,263,263,
-263,263,263,263,263,263,263,263,263,115,263,263,263,263,263,263,
-263,263,263,263,263,263,263,263,263,263,115,115,115,263,261,261,
-261,262,262,262,262,115,261,261,261,115,261,261,261,261,115,115,
-115,115,115,115,115,261,261,115,263,263,263,115,115,115,115,115,
-263,263,261,261,115,115,264,264,264,264,264,264,264,264,264,264,
-115,115,115,115,115,115,115,115,265,265,265,265,265,265,265,266,
+278,279,279,279,115,280,280,280,280,280,280,280,280,115,280,280,
+280,115,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
+280,280,280,280,280,280,280,280,280,115,280,280,280,280,280,280,
+280,280,280,280,280,280,280,280,280,280,115,115,115,280,278,278,
+278,279,279,279,279,115,278,278,278,115,278,278,278,278,115,115,
+115,115,115,115,115,278,278,115,280,280,280,115,115,115,115,115,
+280,280,278,278,115,115,281,281,281,281,281,281,281,281,281,281,
+115,115,115,115,115,115,115,115,282,282,282,282,282,282,282,283,
/* block 25 */
-115,267,268,268,115,269,269,269,269,269,269,269,269,115,269,269,
-269,115,269,269,269,269,269,269,269,269,269,269,269,269,269,269,
-269,269,269,269,269,269,269,269,269,115,269,269,269,269,269,269,
-269,269,269,269,115,269,269,269,269,269,115,115,267,269,268,267,
-268,268,270,268,268,115,267,268,268,115,268,268,267,267,115,115,
-115,115,115,115,115,270,270,115,115,115,115,115,115,115,269,115,
-269,269,267,267,115,115,271,271,271,271,271,271,271,271,271,271,
-115,269,269,115,115,115,115,115,115,115,115,115,115,115,115,115,
+284,285,286,286,115,284,284,284,284,284,284,284,284,115,284,284,
+284,115,284,284,284,284,284,284,284,284,284,284,284,284,284,284,
+284,284,284,284,284,284,284,284,284,115,284,284,284,284,284,284,
+284,284,284,284,115,284,284,284,284,284,115,115,285,284,286,285,
+286,286,287,286,286,115,285,286,286,115,286,286,285,285,115,115,
+115,115,115,115,115,287,287,115,115,115,115,115,115,115,284,115,
+284,284,285,285,115,115,288,288,288,288,288,288,288,288,288,288,
+115,284,284,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 26 */
-115,272,273,273,115,274,274,274,274,274,274,274,274,115,274,274,
-274,115,274,274,274,274,274,274,274,274,274,274,274,274,274,274,
-274,274,274,274,274,274,274,274,274,274,274,274,274,274,274,274,
-274,274,274,274,274,274,274,274,274,274,274,115,115,274,275,273,
-273,272,272,272,272,115,273,273,273,115,273,273,273,272,274,115,
-115,115,115,115,115,115,115,275,115,115,115,115,115,115,115,274,
-274,274,272,272,115,115,276,276,276,276,276,276,276,276,276,276,
-277,277,277,277,277,277,115,115,115,278,274,274,274,274,274,274,
+289,289,290,290,115,291,291,291,291,291,291,291,291,115,291,291,
+291,115,291,291,291,291,291,291,291,291,291,291,291,291,291,291,
+291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,
+291,291,291,291,291,291,291,291,291,291,291,289,289,291,292,290,
+290,289,289,289,289,115,290,290,290,115,290,290,290,289,293,294,
+115,115,115,115,291,291,291,292,295,295,295,295,295,295,295,291,
+291,291,289,289,115,115,296,296,296,296,296,296,296,296,296,296,
+295,295,295,295,295,295,295,295,295,294,291,291,291,291,291,291,
/* block 27 */
-115,115,279,279,115,280,280,280,280,280,280,280,280,280,280,280,
-280,280,280,280,280,280,280,115,115,115,280,280,280,280,280,280,
-280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
-280,280,115,280,280,280,280,280,280,280,280,280,115,280,115,115,
-280,280,280,280,280,280,280,115,115,115,281,115,115,115,115,282,
-279,279,281,281,281,115,281,115,279,279,279,279,279,279,279,282,
-115,115,115,115,115,115,283,283,283,283,283,283,283,283,283,283,
-115,115,279,279,284,115,115,115,115,115,115,115,115,115,115,115,
+115,115,297,297,115,298,298,298,298,298,298,298,298,298,298,298,
+298,298,298,298,298,298,298,115,115,115,298,298,298,298,298,298,
+298,298,298,298,298,298,298,298,298,298,298,298,298,298,298,298,
+298,298,115,298,298,298,298,298,298,298,298,298,115,298,115,115,
+298,298,298,298,298,298,298,115,115,115,299,115,115,115,115,300,
+297,297,299,299,299,115,299,115,297,297,297,297,297,297,297,300,
+115,115,115,115,115,115,301,301,301,301,301,301,301,301,301,301,
+115,115,297,297,302,115,115,115,115,115,115,115,115,115,115,115,
/* block 28 */
-115,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,
-285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,
-285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,
-285,286,285,287,286,286,286,286,286,286,286,115,115,115,115, 5,
-285,285,285,285,285,285,288,286,286,286,286,286,286,286,286,289,
-290,290,290,290,290,290,290,290,290,290,289,289,115,115,115,115,
+115,303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,
+303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,
+303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,303,
+303,304,303,305,304,304,304,304,304,304,304,115,115,115,115, 5,
+303,303,303,303,303,303,306,304,304,304,304,304,304,304,304,307,
+308,308,308,308,308,308,308,308,308,308,307,307,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 29 */
-115,291,291,115,291,115,115,291,291,115,291,115,115,291,115,115,
-115,115,115,115,291,291,291,291,115,291,291,291,291,291,291,291,
-115,291,291,291,115,291,115,291,115,115,291,291,115,291,291,291,
-291,292,291,293,292,292,292,292,292,292,115,292,292,291,115,115,
-291,291,291,291,291,115,294,115,292,292,292,292,292,292,115,115,
-295,295,295,295,295,295,295,295,295,295,115,115,291,291,291,291,
+115,309,309,115,309,115,115,309,309,115,309,115,115,309,115,115,
+115,115,115,115,309,309,309,309,115,309,309,309,309,309,309,309,
+115,309,309,309,115,309,115,309,115,115,309,309,115,309,309,309,
+309,310,309,311,310,310,310,310,310,310,115,310,310,309,115,115,
+309,309,309,309,309,115,312,115,310,310,310,310,310,310,115,115,
+313,313,313,313,313,313,313,313,313,313,115,115,309,309,309,309,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 30 */
-296,297,297,297,298,298,298,298,298,298,298,298,298,298,298,298,
-298,298,298,297,298,297,297,297,299,299,297,297,297,297,297,297,
-300,300,300,300,300,300,300,300,300,300,301,301,301,301,301,301,
-301,301,301,301,297,299,297,299,297,299,302,303,302,303,304,304,
-296,296,296,296,296,296,296,296,115,296,296,296,296,296,296,296,
-296,296,296,296,296,296,296,296,296,296,296,296,296,296,296,296,
-296,296,296,296,296,296,296,296,296,296,296,296,296,115,115,115,
-115,299,299,299,299,299,299,299,299,299,299,299,299,299,299,304,
+314,315,315,315,316,316,316,316,316,316,316,316,316,316,316,316,
+316,316,316,315,316,315,315,315,317,317,315,315,315,315,315,315,
+318,318,318,318,318,318,318,318,318,318,319,319,319,319,319,319,
+319,319,319,319,315,317,315,317,315,317,320,321,320,321,322,322,
+314,314,314,314,314,314,314,314,115,314,314,314,314,314,314,314,
+314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+314,314,314,314,314,314,314,314,314,314,314,314,314,115,115,115,
+115,317,317,317,317,317,317,317,317,317,317,317,317,317,317,322,
/* block 31 */
-299,299,299,299,299,298,299,299,296,296,296,296,296,299,299,299,
-299,299,299,299,299,299,299,299,115,299,299,299,299,299,299,299,
-299,299,299,299,299,299,299,299,299,299,299,299,299,299,299,299,
-299,299,299,299,299,299,299,299,299,299,299,299,299,115,297,297,
-297,297,297,297,297,297,299,297,297,297,297,297,297,115,297,297,
-298,298,298,298,298, 19, 19, 19, 19,298,298,115,115,115,115,115,
+317,317,317,317,317,316,317,317,314,314,314,314,314,317,317,317,
+317,317,317,317,317,317,317,317,115,317,317,317,317,317,317,317,
+317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
+317,317,317,317,317,317,317,317,317,317,317,317,317,115,315,315,
+315,315,315,315,315,315,317,315,315,315,315,315,315,115,315,315,
+316,316,316,316,316, 19, 19, 19, 19,316,316,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 32 */
-305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,
-305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,
-305,305,305,305,305,305,305,305,305,305,305,306,306,307,307,307,
-307,308,307,307,307,307,307,307,306,307,307,308,308,307,307,305,
-309,309,309,309,309,309,309,309,309,309,310,310,310,310,310,310,
-305,305,305,305,305,305,308,308,307,307,305,305,305,305,307,307,
-307,305,306,306,306,305,305,306,306,306,306,306,306,306,305,305,
-305,307,307,307,307,305,305,305,305,305,305,305,305,305,305,305,
+323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
+323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
+323,323,323,323,323,323,323,323,323,323,323,324,324,325,325,325,
+325,326,325,325,325,325,325,325,324,325,325,326,326,325,325,323,
+327,327,327,327,327,327,327,327,327,327,328,328,328,328,328,328,
+323,323,323,323,323,323,326,326,325,325,323,323,323,323,325,325,
+325,323,324,324,324,323,323,324,324,324,324,324,324,324,323,323,
+323,325,325,325,325,323,323,323,323,323,323,323,323,323,323,323,
/* block 33 */
-305,305,307,306,308,307,307,306,306,306,306,306,306,307,305,306,
-309,309,309,309,309,309,309,309,309,309,306,306,306,307,311,311,
-312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,
-312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,
-312,312,312,312,312,312,115,312,115,115,115,115,115,312,115,115,
-313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,
-313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,
-313,313,313,313,313,313,313,313,313,313,313, 4,314,313,313,313,
+323,323,325,324,326,325,325,324,324,324,324,324,324,325,323,324,
+327,327,327,327,327,327,327,327,327,327,324,324,324,325,329,329,
+330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,
+330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,
+330,330,330,330,330,330,115,330,115,115,115,115,115,330,115,115,
+331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,
+331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,331,
+331,331,331,331,331,331,331,331,331,331,331, 4,332,331,331,331,
/* block 34 */
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,
-316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,
+334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,
/* block 35 */
-316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,
-316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,
-316,316,316,316,316,316,316,316,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
+334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,
+334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,
+334,334,334,334,334,334,334,334,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
/* block 36 */
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,115,318,318,318,318,115,115,
-318,318,318,318,318,318,318,115,318,115,318,318,318,318,115,115,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,115,336,336,336,336,115,115,
+336,336,336,336,336,336,336,115,336,115,336,336,336,336,115,115,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
/* block 37 */
-318,318,318,318,318,318,318,318,318,115,318,318,318,318,115,115,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,115,
-318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
+336,336,336,336,336,336,336,336,336,115,336,336,336,336,115,115,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,115,336,336,336,336,115,115,336,336,336,336,336,336,336,115,
+336,115,336,336,336,336,115,115,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
/* block 38 */
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,318,318,318,318,115,115,319,319,319,
-320,320,320,320,320,320,320,320,320,321,321,321,321,321,321,321,
-321,321,321,321,321,321,321,321,321,321,321,321,321,115,115,115,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,115,336,336,336,336,115,115,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,336,336,336,336,115,115,337,337,337,
+338,338,338,338,338,338,338,338,338,339,339,339,339,339,339,339,
+339,339,339,339,339,339,339,339,339,339,339,339,339,115,115,115,
/* block 39 */
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-322,322,322,322,322,322,322,322,322,322,115,115,115,115,115,115,
-323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
-323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
-323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
-323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
-323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
-324,324,324,324,324,324,115,115,325,325,325,325,325,325,115,115,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+340,340,340,340,340,340,340,340,340,340,115,115,115,115,115,115,
+341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,
+341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,
+341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,
+341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,
+341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,341,
+342,342,342,342,342,342,115,115,343,343,343,343,343,343,115,115,
/* block 40 */
-326,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
+344,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
/* block 41 */
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
/* block 42 */
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,328,328,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,346,346,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
/* block 43 */
-329,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,
-330,330,330,330,330,330,330,330,330,330,330,331,332,115,115,115,
-333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
-333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
-333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
-333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
-333,333,333,333,333,333,333,333,333,333,333, 4, 4, 4,334,334,
-334,333,333,333,333,333,333,333,333,115,115,115,115,115,115,115,
+347,348,348,348,348,348,348,348,348,348,348,348,348,348,348,348,
+348,348,348,348,348,348,348,348,348,348,348,349,350,115,115,115,
+351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,
+351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,
+351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,
+351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,351,
+351,351,351,351,351,351,351,351,351,351,351, 4, 4, 4,352,352,
+352,351,351,351,351,351,351,351,351,115,115,115,115,115,115,115,
/* block 44 */
-335,335,335,335,335,335,335,335,335,335,335,335,335,115,335,335,
-335,335,336,336,336,115,115,115,115,115,115,115,115,115,115,115,
-337,337,337,337,337,337,337,337,337,337,337,337,337,337,337,337,
-337,337,338,338,338, 4, 4,115,115,115,115,115,115,115,115,115,
-339,339,339,339,339,339,339,339,339,339,339,339,339,339,339,339,
-339,339,340,340,115,115,115,115,115,115,115,115,115,115,115,115,
-341,341,341,341,341,341,341,341,341,341,341,341,341,115,341,341,
-341,115,342,342,115,115,115,115,115,115,115,115,115,115,115,115,
+353,353,353,353,353,353,353,353,353,353,353,353,353,115,353,353,
+353,353,354,354,354,115,115,115,115,115,115,115,115,115,115,115,
+355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,
+355,355,356,356,356, 4, 4,115,115,115,115,115,115,115,115,115,
+357,357,357,357,357,357,357,357,357,357,357,357,357,357,357,357,
+357,357,358,358,115,115,115,115,115,115,115,115,115,115,115,115,
+359,359,359,359,359,359,359,359,359,359,359,359,359,115,359,359,
+359,115,360,360,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 45 */
-343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,
-343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,
-343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,
-343,343,343,343,344,344,345,344,344,344,344,344,344,344,345,345,
-345,345,345,345,345,345,344,345,345,344,344,344,344,344,344,344,
-344,344,344,344,346,346,346,347,346,346,346,348,343,344,115,115,
-349,349,349,349,349,349,349,349,349,349,115,115,115,115,115,115,
-350,350,350,350,350,350,350,350,350,350,115,115,115,115,115,115,
+361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,
+361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,
+361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,
+361,361,361,361,362,362,363,362,362,362,362,362,362,362,363,363,
+363,363,363,363,363,363,362,363,363,362,362,362,362,362,362,362,
+362,362,362,362,364,364,364,365,364,364,364,366,361,362,115,115,
+367,367,367,367,367,367,367,367,367,367,115,115,115,115,115,115,
+368,368,368,368,368,368,368,368,368,368,115,115,115,115,115,115,
/* block 46 */
-351,351, 4, 4,351, 4,352,351,351,351,351,353,353,353,354,115,
-355,355,355,355,355,355,355,355,355,355,115,115,115,115,115,115,
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,357,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,115,115,115,115,115,115,115,115,
+369,369, 4, 4,369, 4,370,369,369,369,369,371,371,371,372,115,
+373,373,373,373,373,373,373,373,373,373,115,115,115,115,115,115,
+374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,375,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,115,115,115,115,115,115,115,115,
/* block 47 */
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,
-356,356,356,356,356,356,356,356,356,353,356,115,115,115,115,115,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,
-327,327,327,327,327,327,115,115,115,115,115,115,115,115,115,115,
+374,374,374,374,374,371,371,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,371,374,115,115,115,115,115,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,115,115,115,115,115,115,115,115,115,115,
/* block 48 */
-358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,
-358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,115,
-359,359,359,360,360,360,360,359,359,360,360,360,115,115,115,115,
-360,360,359,360,360,360,360,360,360,359,359,359,115,115,115,115,
-361,115,115,115,362,362,363,363,363,363,363,363,363,363,363,363,
-364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,
-364,364,364,364,364,364,364,364,364,364,364,364,364,364,115,115,
-364,364,364,364,364,115,115,115,115,115,115,115,115,115,115,115,
+376,376,376,376,376,376,376,376,376,376,376,376,376,376,376,376,
+376,376,376,376,376,376,376,376,376,376,376,376,376,376,376,115,
+377,377,377,378,378,378,378,377,377,378,378,378,115,115,115,115,
+378,378,377,378,378,378,378,378,378,377,377,377,115,115,115,115,
+379,115,115,115,380,380,381,381,381,381,381,381,381,381,381,381,
+382,382,382,382,382,382,382,382,382,382,382,382,382,382,382,382,
+382,382,382,382,382,382,382,382,382,382,382,382,382,382,115,115,
+382,382,382,382,382,115,115,115,115,115,115,115,115,115,115,115,
/* block 49 */
-365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,
-365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,
-365,365,365,365,365,365,365,365,365,365,365,365,115,115,115,115,
-365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,
-365,365,365,365,365,365,365,365,365,365,115,115,115,115,115,115,
-366,366,366,366,366,366,366,366,366,366,367,115,115,115,368,368,
-369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,
-369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,
+383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,
+383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,
+383,383,383,383,383,383,383,383,383,383,383,383,115,115,115,115,
+383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,
+383,383,383,383,383,383,383,383,383,383,115,115,115,115,115,115,
+384,384,384,384,384,384,384,384,384,384,385,115,115,115,386,386,
+387,387,387,387,387,387,387,387,387,387,387,387,387,387,387,387,
+387,387,387,387,387,387,387,387,387,387,387,387,387,387,387,387,
/* block 50 */
-370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,
-370,370,370,370,370,370,370,371,371,372,372,371,115,115,373,373,
-374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
-374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
-374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,
-374,374,374,374,374,375,376,375,376,376,376,376,376,376,376,115,
-376,377,376,377,377,376,376,376,376,376,376,376,376,375,375,375,
-375,375,375,376,376,376,376,376,376,376,376,376,376,115,115,376,
+388,388,388,388,388,388,388,388,388,388,388,388,388,388,388,388,
+388,388,388,388,388,388,388,389,389,390,390,389,115,115,391,391,
+392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,
+392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,
+392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,
+392,392,392,392,392,393,394,393,394,394,394,394,394,394,394,115,
+394,395,394,395,395,394,394,394,394,394,394,394,394,393,393,393,
+393,393,393,394,394,394,394,394,394,394,394,394,394,115,115,394,
/* block 51 */
-378,378,378,378,378,378,378,378,378,378,115,115,115,115,115,115,
-378,378,378,378,378,378,378,378,378,378,115,115,115,115,115,115,
-379,379,379,379,379,379,379,380,379,379,379,379,379,379,115,115,
-110,110,110,110,110,110,110,110,110,110,110,110,110,110,381,115,
+396,396,396,396,396,396,396,396,396,396,115,115,115,115,115,115,
+396,396,396,396,396,396,396,396,396,396,115,115,115,115,115,115,
+397,397,397,397,397,397,397,398,397,397,397,397,397,397,115,115,
+110,110,110,110,110,110,110,110,110,110,110,110,110,110,399,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 52 */
-382,382,382,382,383,384,384,384,384,384,384,384,384,384,384,384,
-384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,
-384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,
-384,384,384,384,382,383,382,382,382,382,382,383,382,383,383,383,
-383,383,382,383,383,384,384,384,384,384,384,384,115,115,115,115,
-385,385,385,385,385,385,385,385,385,385,386,386,386,386,386,386,
-386,387,387,387,387,387,387,387,387,387,387,382,382,382,382,382,
-382,382,382,382,387,387,387,387,387,387,387,387,387,115,115,115,
+400,400,400,400,401,402,402,402,402,402,402,402,402,402,402,402,
+402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,
+402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,
+402,402,402,402,400,401,400,400,400,400,400,401,400,401,401,401,
+401,401,400,401,401,402,402,402,402,402,402,402,115,115,115,115,
+403,403,403,403,403,403,403,403,403,403,404,404,404,404,404,404,
+404,405,405,405,405,405,405,405,405,405,405,400,400,400,400,400,
+400,400,400,400,405,405,405,405,405,405,405,405,405,115,115,115,
/* block 53 */
-388,388,389,390,390,390,390,390,390,390,390,390,390,390,390,390,
-390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,
-390,389,388,388,388,388,389,389,388,388,389,388,388,388,390,390,
-391,391,391,391,391,391,391,391,391,391,390,390,390,390,390,390,
-392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,
-392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,
-392,392,392,392,392,392,393,394,393,393,394,394,394,393,394,393,
-393,393,394,394,115,115,115,115,115,115,115,115,395,395,395,395,
+406,406,407,408,408,408,408,408,408,408,408,408,408,408,408,408,
+408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,
+408,407,406,406,406,406,407,407,406,406,407,406,406,406,408,408,
+409,409,409,409,409,409,409,409,409,409,408,408,408,408,408,408,
+410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,
+410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,
+410,410,410,410,410,410,411,412,411,411,412,412,412,411,412,411,
+411,411,412,412,115,115,115,115,115,115,115,115,413,413,413,413,
/* block 54 */
-396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,
-396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,
-396,396,396,396,397,397,397,397,397,397,397,397,398,398,398,398,
-398,398,398,398,397,397,398,398,115,115,115,399,399,399,399,399,
-400,400,400,400,400,400,400,400,400,400,115,115,115,396,396,396,
-401,401,401,401,401,401,401,401,401,401,402,402,402,402,402,402,
-402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,
-402,402,402,402,402,402,402,402,403,403,403,403,403,403,404,404,
+414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,
+414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,
+414,414,414,414,415,415,415,415,415,415,415,415,416,416,416,416,
+416,416,416,416,415,415,416,416,115,115,115,417,417,417,417,417,
+418,418,418,418,418,418,418,418,418,418,115,115,115,414,414,414,
+419,419,419,419,419,419,419,419,419,419,420,420,420,420,420,420,
+420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,420,
+420,420,420,420,420,420,420,420,421,421,421,421,421,421,422,422,
/* block 55 */
+423,424,425,426,427,428,429,430,431,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-405,405,405,405,405,405,405,405,115,115,115,115,115,115,115,115,
+432,432,432,432,432,432,432,432,115,115,115,115,115,115,115,115,
110,110,110, 4,110,110,110,110,110,110,110,110,110,110,110,110,
-110,406,110,110,110,110,110,110,110,407,407,407,407,110,407,407,
-407,407,406,406,110,407,407,115,110,110,115,115,115,115,115,115,
+110,433,110,110,110,110,110,110,110,434,434,434,434,110,434,434,
+434,434,433,433,110,434,434,433,110,110,115,115,115,115,115,115,
/* block 56 */
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33,123,123,123,123,123,408,107,107,107,107,
+ 33, 33, 33, 33, 33, 33,123,123,123,123,123,435,107,107,107,107,
107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
107,107,107,107,107,107,107,107,107,107,107,107,107,116,116,116,
116,116,107,107,107,107,116,116,116,116,116, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 33,409,410, 33, 33, 33,411, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,436,437, 33, 33, 33,438, 33, 33,
/* block 57 */
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
@@ -1955,7 +2054,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
-110,110,110,110,110,110,115,115,115,115,115,115,110,110,110,110,
+110,110,110,110,110,110,110,110,110,110,115,110,110,110,110,110,
/* block 58 */
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
@@ -1964,12 +2063,12 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
-412,413, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
+439,440, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
/* block 59 */
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
- 30, 31, 30, 31, 30, 31, 33, 33, 33, 33, 33,414, 33, 33,415, 33,
+ 30, 31, 30, 31, 30, 31, 33, 33, 33, 33, 33,441, 33, 33,442, 33,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
@@ -1978,57 +2077,57 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
/* block 60 */
-416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417,
-416,416,416,416,416,416,115,115,417,417,417,417,417,417,115,115,
-416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417,
-416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417,
-416,416,416,416,416,416,115,115,417,417,417,417,417,417,115,115,
-123,416,123,416,123,416,123,416,115,417,115,417,115,417,115,417,
-416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417,
-418,418,419,419,419,419,420,420,421,421,422,422,423,423,115,115,
+443,443,443,443,443,443,443,443,444,444,444,444,444,444,444,444,
+443,443,443,443,443,443,115,115,444,444,444,444,444,444,115,115,
+443,443,443,443,443,443,443,443,444,444,444,444,444,444,444,444,
+443,443,443,443,443,443,443,443,444,444,444,444,444,444,444,444,
+443,443,443,443,443,443,115,115,444,444,444,444,444,444,115,115,
+123,443,123,443,123,443,123,443,115,444,115,444,115,444,115,444,
+443,443,443,443,443,443,443,443,444,444,444,444,444,444,444,444,
+445,445,446,446,446,446,447,447,448,448,449,449,450,450,115,115,
/* block 61 */
-416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424,
-416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424,
-416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424,
-416,416,123,425,123,115,123,123,417,417,426,426,427,114,428,114,
-114,114,123,425,123,115,123,123,429,429,429,429,427,114,114,114,
-416,416,123,123,115,115,123,123,417,417,430,430,115,114,114,114,
-416,416,123,123,123,164,123,123,417,417,431,431,169,114,114,114,
-115,115,123,425,123,115,123,123,432,432,433,433,427,114,114,115,
+443,443,443,443,443,443,443,443,451,451,451,451,451,451,451,451,
+443,443,443,443,443,443,443,443,451,451,451,451,451,451,451,451,
+443,443,443,443,443,443,443,443,451,451,451,451,451,451,451,451,
+443,443,123,452,123,115,123,123,444,444,453,453,454,114,455,114,
+114,114,123,452,123,115,123,123,456,456,456,456,454,114,114,114,
+443,443,123,123,115,115,123,123,444,444,457,457,115,114,114,114,
+443,443,123,123,123,164,123,123,444,444,458,458,169,114,114,114,
+115,115,123,452,123,115,123,123,459,459,460,460,454,114,114,115,
/* block 62 */
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22,434,434, 22, 22,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22,461,462, 22, 22,
9, 9, 9, 9, 9, 9, 4, 4, 21, 25, 6, 21, 21, 25, 6, 21,
- 4, 4, 4, 4, 4, 4, 4, 4,435,436, 22, 22, 22, 22, 22, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4,463,464, 22, 22, 22, 22, 22, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 21, 25, 4, 4, 4, 4, 15,
15, 4, 4, 4, 8, 6, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 8, 4, 15, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3,
- 22, 22, 22, 22, 22,437, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22,465, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
23,107,115,115, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,107,
/* block 63 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,115,
107,107,107,107,107,107,107,107,107,107,107,107,107,115,115,115,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,115,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-110,110,110,110,110,110,110,110,110,110,110,110,110,381,381,381,
-381,110,381,381,381,110,110,110,110,110,110,110,110,110,110,110,
+110,110,110,110,110,110,110,110,110,110,110,110,110,399,399,399,
+399,110,399,399,399,110,110,110,110,110,110,110,110,110,110,110,
110,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 64 */
- 19, 19,438, 19, 19, 19, 19,438, 19, 19,439,438,438,438,439,439,
-438,438,438,439, 19,438, 19, 19, 8,438,438,438,438,438, 19, 19,
- 19, 19, 19, 19,438, 19,440, 19,438, 19,441,442,438,438, 19,439,
-438,438,443,438,439,407,407,407,407,439, 19, 19,439,439,438,438,
- 8, 8, 8, 8, 8,438,439,439,439,439, 19, 8, 19, 19,444, 19,
+ 19, 19,466, 19, 19, 19, 19,466, 19, 19,467,466,466,466,467,467,
+466,466,466,467, 19,466, 19, 19, 8,466,466,466,466,466, 19, 19,
+ 19, 19, 19, 19,466, 19,468, 19,466, 19,469,470,466,466, 19,467,
+466,466,471,466,467,434,434,434,434,467, 19, 19,467,467,466,466,
+ 8, 8, 8, 8, 8,466,467,467,467,467, 19, 8, 19, 19,472, 19,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
-445,445,445,445,445,445,445,445,445,445,445,445,445,445,445,445,
-446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,
+473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
+474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,
/* block 65 */
-447,447,447, 30, 31,447,447,447,447, 23, 19, 19,115,115,115,115,
+475,475,475, 30, 31,475,475,475,475, 23, 19, 19,115,115,115,115,
8, 8, 8, 8, 8, 19, 19, 19, 19, 19, 8, 8, 19, 19, 19, 19,
8, 19, 19, 8, 19, 19, 8, 19, 19, 19, 19, 19, 19, 19, 8, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -2065,7 +2164,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8,
8, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
/* block 69 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -2081,10 +2180,10 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19,448,448,448,448,448,448,448,448,448,448,
-448,448,448,448,448,448,448,448,448,448,448,448,448,448,448,448,
-449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,
-449,449,449,449,449,449,449,449,449,449, 23, 23, 23, 23, 23, 23,
+ 19, 19, 19, 19, 19, 19,476,476,476,476,476,476,476,476,476,476,
+476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,
+477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
+477,477,477,477,477,477,477,477,477,477, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
/* block 71 */
@@ -2109,25 +2208,35 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
/* block 73 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,478, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+479, 19,479, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
/* block 74 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19,479,479, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7,
- 6, 7, 6, 7, 6, 7, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19,478, 19, 19, 19, 19, 19, 19,
/* block 75 */
+ 19, 19, 19, 19, 19, 19, 19, 19,479, 19,478,478,478,478, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19,479, 19, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7,
+ 6, 7, 6, 7, 6, 7, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+
+/* block 76 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -2137,17 +2246,17 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
8, 8, 8, 8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-/* block 76 */
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,
-
/* block 77 */
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,
+
+/* block 78 */
8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6,
7, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
@@ -2157,7 +2266,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 7, 8, 8,
-/* block 78 */
+/* block 79 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -2167,257 +2276,257 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 79 */
+/* block 80 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19,115, 19, 19, 19, 19, 19, 19,
- 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115, 19, 19, 19, 19,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 80 */
-451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,
-451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,
-451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,115,
-452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,
-452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,
-452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,115,
- 30, 31,453,454,455,456,457, 30, 31, 30, 31, 30, 31,458,459,460,
-461, 33, 30, 31, 33, 30, 31, 33, 33, 33, 33, 33,107,107,462,462,
-
/* block 81 */
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,115,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,115,
+ 30, 31,483,484,485,486,487, 30, 31, 30, 31, 30, 31,488,489,490,
+491, 33, 30, 31, 33, 30, 31, 33, 33, 33, 33, 33,107,107,492,492,
+
+/* block 82 */
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161,
-160,161,160,161,463,464,464,464,464,464,464,160,161,160,161,465,
-465,465,160,161,115,115,115,115,115,466,466,466,466,467,466,466,
-
-/* block 82 */
-468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,
-468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,
-468,468,468,468,468,468,115,468,115,115,115,115,115,468,115,115,
-469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,
-469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,
-469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,
-469,469,469,469,469,469,469,469,115,115,115,115,115,115,115,470,
-471,115,115,115,115,115,115,115,115,115,115,115,115,115,115,472,
+160,161,160,161,493,494,494,494,494,494,494,160,161,160,161,495,
+495,495,160,161,115,115,115,115,115,496,496,496,496,497,496,496,
/* block 83 */
-318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,
-318,318,318,318,318,318,318,115,115,115,115,115,115,115,115,115,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115,
-178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,
-178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,
+498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
+498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
+498,498,498,498,498,498,115,498,115,115,115,115,115,498,115,115,
+499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,
+499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,
+499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,
+499,499,499,499,499,499,499,499,115,115,115,115,115,115,115,500,
+501,115,115,115,115,115,115,115,115,115,115,115,115,115,115,502,
/* block 84 */
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,336,336,336,336,336,336,115,115,115,115,115,115,115,115,115,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,115,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,115,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,115,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,115,
+192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,
+192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,
+
+/* block 85 */
4, 4, 21, 25, 21, 25, 4, 4, 4, 21, 25, 4, 21, 25, 4, 4,
4, 4, 4, 4, 4, 4, 4, 9, 4, 4, 9, 4, 21, 25, 4, 4,
21, 25, 6, 7, 6, 7, 6, 7, 6, 7, 4, 4, 4, 4, 4,108,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9, 4, 4, 4, 4,
- 9, 4, 6,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 9, 4, 6, 4, 4, 4, 4, 4, 4, 4,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 85 */
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,115,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,115,115,115,115,115,115,115,115,115,115,115,115,
-
/* block 86 */
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,115,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 87 */
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,
-473,473,473,473,473,473,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
/* block 88 */
- 3, 4, 4, 4, 19,474,407,475, 6, 7, 6, 7, 6, 7, 6, 7,
- 6, 7, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, 9, 6, 7, 7,
- 19,475,475,475,475,475,475,475,475,475,110,110,110,110,476,476,
- 9,108,108,108,108,108, 19, 19,475,475,475,474,407, 4, 19, 19,
-115,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
-477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
-477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
-477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,
+503,503,503,503,503,503,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
/* block 89 */
-477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,
-477,477,477,477,477,477,477,115,115,110,110, 14, 14,478,478,477,
- 9,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479, 4,108,480,480,479,
+ 3, 4, 4, 4, 19,504,434,505, 6, 7, 6, 7, 6, 7, 6, 7,
+ 6, 7, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, 9, 6, 7, 7,
+ 19,505,505,505,505,505,505,505,505,505,110,110,110,110,506,506,
+ 9,108,108,108,108,108, 19, 19,505,505,505,504,434, 4, 19, 19,
+115,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
/* block 90 */
-115,115,115,115,115,481,481,481,481,481,481,481,481,481,481,481,
-481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
-481,481,481,481,481,481,481,481,481,481,481,481,481,481,115,115,
-115,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,115,115,110,110, 14, 14,508,508,507,
+ 9,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509, 4,108,510,510,509,
/* block 91 */
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,115,
+115,115,115,115,115,511,511,511,511,511,511,511,511,511,511,511,
+511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,
+511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,115,
+115,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+
+/* block 92 */
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,115,
19, 19, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
-481,481,481,481,481,481,481,481,481,481,481,115,115,115,115,115,
+511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,
+511,511,511,511,511,511,511,511,511,511,511,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
-/* block 92 */
-483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
-483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,115,
+/* block 93 */
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,115,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 23, 23, 23, 23, 23, 23, 23, 23,
19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
-483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
-483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, 19,
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513, 19,
-/* block 93 */
+/* block 94 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,115,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,115,
-/* block 94 */
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,
-484,484,484,484,484,484,484,484, 19, 19, 19, 19, 19, 19, 19, 19,
+/* block 95 */
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,514,
+514,514,514,514,514,514,514,514, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 95 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-
/* block 96 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,115,115,115,115,115,115,115,115,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+
+/* block 97 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,115,115,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 97 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-
/* block 98 */
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,487,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 99 */
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
-486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,517,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
/* block 100 */
-486,486,486,486,486,486,486,486,486,486,486,486,486,115,115,115,
-488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,
-488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,
-488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,
-488,488,488,488,488,488,488,115,115,115,115,115,115,115,115,115,
-489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,
-489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,
-489,489,489,489,489,489,489,489,490,490,490,490,490,490,491,491,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
+516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
/* block 101 */
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
+516,516,516,516,516,516,516,516,516,516,516,516,516,115,115,115,
+518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,
+518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,
+518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,
+518,518,518,518,518,518,518,115,115,115,115,115,115,115,115,115,
+519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,
+519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,
+519,519,519,519,519,519,519,519,520,520,520,520,520,520,521,521,
/* block 102 */
-492,492,492,492,492,492,492,492,492,492,492,492,493,494,494,494,
-492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,
-495,495,495,495,495,495,495,495,495,495,492,492,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,496,178,
-179,179,179,497,178,178,178,178,178,178,178,178,178,178,497,409,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
/* block 103 */
-175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176,
-175,176,175,176,175,176,175,176,175,176,175,176,409,409,178,178,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,499,499,499,499,499,499,499,499,499,499,
-500,500,501,501,501,501,501,501,115,115,115,115,115,115,115,115,
+522,522,522,522,522,522,522,522,522,522,522,522,523,524,524,524,
+522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,
+525,525,525,525,525,525,525,525,525,525,522,522,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+187,188,187,188,187,188,187,188,187,188,526,527,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,528,192,
+193,193,193,529,192,192,192,192,192,192,192,192,192,192,529,436,
/* block 104 */
+187,188,187,188,187,188,187,188,187,188,187,188,187,188,187,188,
+187,188,187,188,187,188,187,188,187,188,187,188,436,436,192,192,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,531,531,531,531,531,531,531,531,531,531,
+532,532,533,533,533,533,533,533,115,115,115,115,115,115,115,115,
+
+/* block 105 */
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14,108,108,108,108,108,108,108,108,108,
14, 14, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
@@ -2425,349 +2534,349 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
-107, 33, 33, 33, 33, 33, 33, 33, 33, 30, 31, 30, 31,502, 30, 31,
+107, 33, 33, 33, 33, 33, 33, 33, 33, 30, 31, 30, 31,534, 30, 31,
-/* block 105 */
- 30, 31, 30, 31, 30, 31, 30, 31,108, 14, 14, 30, 31,503, 33, 20,
+/* block 106 */
+ 30, 31, 30, 31, 30, 31, 30, 31,108, 14, 14, 30, 31,535, 33, 20,
30, 31, 30, 31, 33, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,
- 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,504,505,506,507,115,115,
-508,509,510,511, 30, 31, 30, 31,115,115,115,115,115,115,115,115,
+ 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,536,537,538,539,536,115,
+540,541,542,543, 30, 31, 30, 31,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115, 20,107,107, 33, 20, 20, 20, 20, 20,
-/* block 106 */
-512,512,513,512,512,512,513,512,512,512,512,513,512,512,512,512,
-512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
-512,512,512,514,514,513,513,514,515,515,515,515,115,115,115,115,
- 23, 23, 23, 23, 23, 23, 19, 19, 5, 19,115,115,115,115,115,115,
-516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
-516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
-516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,
-516,516,516,516,517,517,517,517,115,115,115,115,115,115,115,115,
-
/* block 107 */
-518,518,519,519,519,519,519,519,519,519,519,519,519,519,519,519,
-519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,
-519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,
-519,519,519,519,518,518,518,518,518,518,518,518,518,518,518,518,
-518,518,518,518,520,115,115,115,115,115,115,115,115,115,521,521,
-522,522,522,522,522,522,522,522,522,522,115,115,115,115,115,115,
-222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,
-222,222,224,224,224,224,224,224,226,226,226,224,226,224,115,115,
+544,544,545,544,544,544,545,544,544,544,544,545,544,544,544,544,
+544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,
+544,544,544,546,546,545,545,546,547,547,547,547,115,115,115,115,
+ 23, 23, 23, 23, 23, 23, 19, 19, 5, 19,115,115,115,115,115,115,
+548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,
+548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,
+548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,
+548,548,548,548,549,549,549,549,115,115,115,115,115,115,115,115,
/* block 108 */
-523,523,523,523,523,523,523,523,523,523,524,524,524,524,524,524,
-524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,
-524,524,524,524,524,524,525,525,525,525,525,525,525,525, 4,526,
-527,527,527,527,527,527,527,527,527,527,527,527,527,527,527,527,
-527,527,527,527,527,527,527,528,528,528,528,528,528,528,528,528,
-528,528,529,529,115,115,115,115,115,115,115,115,115,115,115,530,
-315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,
-315,315,315,315,315,315,315,315,315,315,315,315,315,115,115,115,
+550,550,551,551,551,551,551,551,551,551,551,551,551,551,551,551,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,
+551,551,551,551,550,550,550,550,550,550,550,550,550,550,550,550,
+550,550,550,550,552,552,115,115,115,115,115,115,115,115,553,553,
+554,554,554,554,554,554,554,554,554,554,115,115,115,115,115,115,
+238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,
+238,238,240,240,240,240,240,240,242,242,242,240,242,240,115,115,
/* block 109 */
-531,531,531,532,533,533,533,533,533,533,533,533,533,533,533,533,
-533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,
-533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,
-533,533,533,531,532,532,531,531,531,531,532,532,531,532,532,532,
-532,534,534,534,534,534,534,534,534,534,534,534,534,534,115,108,
-535,535,535,535,535,535,535,535,535,535,115,115,115,115,534,534,
-305,305,305,305,305,307,536,305,305,305,305,305,305,305,305,305,
-309,309,309,309,309,309,309,309,309,309,305,305,305,305,305,115,
+555,555,555,555,555,555,555,555,555,555,556,556,556,556,556,556,
+556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
+556,556,556,556,556,556,557,557,557,557,557,557,557,557, 4,558,
+559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,
+559,559,559,559,559,559,559,560,560,560,560,560,560,560,560,560,
+560,560,561,561,115,115,115,115,115,115,115,115,115,115,115,562,
+333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,
+333,333,333,333,333,333,333,333,333,333,333,333,333,115,115,115,
/* block 110 */
-537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,
-537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,
-537,537,537,537,537,537,537,537,537,538,538,538,538,538,538,539,
-539,538,538,539,539,538,538,115,115,115,115,115,115,115,115,115,
-537,537,537,538,537,537,537,537,537,537,537,537,538,539,115,115,
-540,540,540,540,540,540,540,540,540,540,115,115,541,541,541,541,
-305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,
-536,305,305,305,305,305,305,311,311,311,305,306,307,306,305,305,
+563,563,563,564,565,565,565,565,565,565,565,565,565,565,565,565,
+565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,
+565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,
+565,565,565,563,564,564,563,563,563,563,564,564,563,564,564,564,
+564,566,566,566,566,566,566,566,566,566,566,566,566,566,115,108,
+567,567,567,567,567,567,567,567,567,567,115,115,115,115,566,566,
+323,323,323,323,323,325,568,323,323,323,323,323,323,323,323,323,
+327,327,327,327,327,327,327,327,327,327,323,323,323,323,323,115,
/* block 111 */
-542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,
-542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,
-542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,
-543,542,543,543,543,542,542,543,543,542,542,542,542,542,543,543,
-542,543,542,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,542,542,544,545,545,
-546,546,546,546,546,546,546,546,546,546,546,547,548,548,547,547,
-549,549,546,550,550,547,548,115,115,115,115,115,115,115,115,115,
+569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,
+569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,
+569,569,569,569,569,569,569,569,569,570,570,570,570,570,570,571,
+571,570,570,571,571,570,570,115,115,115,115,115,115,115,115,115,
+569,569,569,570,569,569,569,569,569,569,569,569,570,571,115,115,
+572,572,572,572,572,572,572,572,572,572,115,115,573,573,573,573,
+323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,
+568,323,323,323,323,323,323,329,329,329,323,324,325,324,323,323,
/* block 112 */
-115,318,318,318,318,318,318,115,115,318,318,318,318,318,318,115,
-115,318,318,318,318,318,318,115,115,115,115,115,115,115,115,115,
-318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115,
+574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,
+574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,
+574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,
+575,574,575,575,575,574,574,575,575,574,574,574,574,574,575,575,
+574,575,574,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,574,574,576,577,577,
+578,578,578,578,578,578,578,578,578,578,578,579,580,580,579,579,
+581,581,578,582,582,579,580,115,115,115,115,115,115,115,115,115,
+
+/* block 113 */
+115,336,336,336,336,336,336,115,115,336,336,336,336,336,336,115,
+115,336,336,336,336,336,336,115,115,115,115,115,115,115,115,115,
+336,336,336,336,336,336,336,115,336,336,336,336,336,336,336,115,
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
- 33, 33, 33,551, 33, 33, 33, 33, 33, 33, 33, 14,107,107,107,107,
+ 33, 33, 33,583, 33, 33, 33, 33, 33, 33, 33, 14,107,107,107,107,
33, 33, 33, 33, 33,123,115,115,115,115,115,115,115,115,115,115,
-552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
-
-/* block 113 */
-552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
-552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
-552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
-552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
-546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,
-546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,
-546,546,546,547,547,548,547,547,548,547,547,549,547,548,115,115,
-553,553,553,553,553,553,553,553,553,553,115,115,115,115,115,115,
+584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
/* block 114 */
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
+584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
+584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
+584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
+584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
+578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,
+578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,
+578,578,578,579,579,580,579,579,580,579,579,581,579,580,115,115,
+585,585,585,585,585,585,585,585,585,585,115,115,115,115,115,115,
/* block 115 */
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
/* block 116 */
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
/* block 117 */
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
/* block 118 */
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
/* block 119 */
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
/* block 120 */
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
/* block 121 */
-555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555,
-555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,
-555,555,555,555,115,115,115,115,115,115,115,115,115,115,115,115,
-316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,
-316,316,316,316,316,316,316,115,115,115,115,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,
-317,317,317,317,317,317,317,317,317,317,317,317,115,115,115,115,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+586,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,586,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,586,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
/* block 122 */
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
-556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,
+587,587,587,587,587,587,587,587,586,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,115,115,115,115,115,115,115,115,115,115,115,115,
+334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,
+334,334,334,334,334,334,334,115,115,115,115,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,115,115,115,115,
/* block 123 */
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
+588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
/* block 124 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
/* block 125 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
/* block 126 */
- 33, 33, 33, 33, 33, 33, 33,115,115,115,115,115,115,115,115,115,
-115,115,115,186,186,186,186,186,115,115,115,115,115,193,190,193,
-193,193,193,193,193,193,193,193,193,558,193,193,193,193,193,193,
-193,193,193,193,193,193,193,115,193,193,193,193,193,115,193,115,
-193,193,115,193,193,115,193,193,193,193,193,193,193,193,193,193,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 127 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,559,559,559,559,559,559,559,559,559,559,559,559,559,559,
-559,559,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+ 33, 33, 33, 33, 33, 33, 33,115,115,115,115,115,115,115,115,115,
+115,115,115,200,200,200,200,200,115,115,115,115,115,207,204,207,
+207,207,207,207,207,207,207,207,207,590,207,207,207,207,207,207,
+207,207,207,207,207,207,207,115,207,207,207,207,207,115,207,115,
+207,207,115,207,207,115,207,207,207,207,207,207,207,207,207,207,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 128 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
+591,591,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 129 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200, 7, 6,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 130 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-115,115,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216, 7, 6,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-200,200,200,200,200,200,200,200,200,200,200,200,197,198,115,115,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
/* block 131 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+115,115,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+216,216,216,216,216,216,216,216,216,216,216,216,212,213,115,115,
+
+/* block 132 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
4, 4, 4, 4, 4, 4, 4, 6, 7, 4,115,115,115,115,115,115,
-110,110,110,110,110,110,110,110,110,110,110,110,110,110,178,178,
+110,110,110,110,110,110,110,110,110,110,110,110,110,110,192,192,
4, 9, 9, 15, 15, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6,
7, 6, 7, 6, 7, 4, 4, 6, 7, 4, 4, 4, 4, 15, 15, 15,
4, 4, 4,115, 4, 4, 4, 4, 9, 6, 7, 6, 7, 6, 7, 4,
4, 4, 8, 9, 8, 8, 8,115, 4, 5, 4, 4,115,115,115,115,
-200,200,200,200,200,115,200,200,200,200,200,200,200,200,200,200,
-
-/* block 132 */
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,115,115, 22,
+216,216,216,216,216,115,216,216,216,216,216,216,216,216,216,216,
/* block 133 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,115,115, 22,
+
+/* block 134 */
115, 4, 4, 4, 5, 4, 4, 4, 6, 7, 4, 8, 4, 9, 4, 4,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 4, 8, 8, 8, 4,
4, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 6, 4, 7, 14, 15,
14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 6, 8, 7, 8, 6,
- 7, 4, 6, 7, 4, 4,479,479,479,479,479,479,479,479,479,479,
-108,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
+ 7, 4, 6, 7, 4, 4,509,509,509,509,509,509,509,509,509,509,
+108,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
-/* block 134 */
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,
-479,479,479,479,479,479,479,479,479,479,479,479,479,479,560,560,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
-482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,115,
-115,115,482,482,482,482,482,482,115,115,482,482,482,482,482,482,
-115,115,482,482,482,482,482,482,115,115,482,482,482,115,115,115,
+/* block 135 */
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,592,592,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
+512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,115,
+115,115,512,512,512,512,512,512,115,115,512,512,512,512,512,512,
+115,115,512,512,512,512,512,512,115,115,512,512,512,115,115,115,
5, 5, 8, 14, 19, 5, 5,115, 19, 8, 8, 8, 8, 19, 19,115,
-437,437,437,437,437,437,437,437,437, 22, 22, 22, 19, 19,115,115,
+465,465,465,465,465,465,465,465,465, 22, 22, 22, 19, 19,115,115,
-/* block 135 */
-561,561,561,561,561,561,561,561,561,561,561,561,115,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,115,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,115,561,561,115,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,115,115,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,115,115,
+/* block 136 */
+593,593,593,593,593,593,593,593,593,593,593,593,115,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,115,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,115,593,593,115,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,115,115,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 136 */
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,
-561,561,561,561,561,561,561,561,561,561,561,115,115,115,115,115,
-
/* block 137 */
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,593,
+593,593,593,593,593,593,593,593,593,593,593,115,115,115,115,115,
+
+/* block 138 */
4, 4, 4,115,115,115,115, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
-562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
-562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
-562,562,562,562,562,563,563,563,563,564,564,564,564,564,564,564,
+594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,
+594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,
+594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,
+594,594,594,594,594,595,595,595,595,596,596,596,596,596,596,596,
-/* block 138 */
-564,564,564,564,564,564,564,564,564,564,563,563,564,115,115,115,
+/* block 139 */
+596,596,596,596,596,596,596,596,596,596,595,595,596,596,596,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
-564,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+596,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,110,115,115,
-/* block 139 */
+/* block 140 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
@@ -2777,479 +2886,509 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 140 */
-565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,
-565,565,565,565,565,565,565,565,565,565,565,565,565,115,115,115,
-566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,
-566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,
-566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,
-566,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 141 */
+597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,
+597,597,597,597,597,597,597,597,597,597,597,597,597,115,115,115,
+598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,
+598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,
+598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,
+598,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
110, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,115,115,115,115,
-/* block 141 */
-567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,
-567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,
-568,568,568,568,115,115,115,115,115,115,115,115,115,115,115,115,
-569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,
-569,570,569,569,569,569,569,569,569,569,570,115,115,115,115,115,
-571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,
-571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,
-571,571,571,571,571,571,572,572,572,572,572,115,115,115,115,115,
-
/* block 142 */
-573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,
-573,573,573,573,573,573,573,573,573,573,573,573,573,573,115,574,
-575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,
-575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,
-575,575,575,575,115,115,115,115,575,575,575,575,575,575,575,575,
-576,577,577,577,577,577,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+599,599,599,599,599,599,599,599,599,599,599,599,599,599,599,599,
+599,599,599,599,599,599,599,599,599,599,599,599,599,599,599,599,
+600,600,600,600,115,115,115,115,115,115,115,115,115,599,599,599,
+601,601,601,601,601,601,601,601,601,601,601,601,601,601,601,601,
+601,602,601,601,601,601,601,601,601,601,602,115,115,115,115,115,
+603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,
+603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,
+603,603,603,603,603,603,604,604,604,604,604,115,115,115,115,115,
/* block 143 */
-578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,
-578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,
-578,578,578,578,578,578,578,578,579,579,579,579,579,579,579,579,
-579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,
-579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,
-580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,
-580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,
-580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,
-
-/* block 144 */
-581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,
-581,581,581,581,581,581,581,581,581,581,581,581,581,581,115,115,
-582,582,582,582,582,582,582,582,582,582,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,
+605,605,605,605,605,605,605,605,605,605,605,605,605,605,115,606,
+607,607,607,607,607,607,607,607,607,607,607,607,607,607,607,607,
+607,607,607,607,607,607,607,607,607,607,607,607,607,607,607,607,
+607,607,607,607,115,115,115,115,607,607,607,607,607,607,607,607,
+608,609,609,609,609,609,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 144 */
+610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,
+610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,
+610,610,610,610,610,610,610,610,611,611,611,611,611,611,611,611,
+611,611,611,611,611,611,611,611,611,611,611,611,611,611,611,611,
+611,611,611,611,611,611,611,611,611,611,611,611,611,611,611,611,
+612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,
+612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,
+612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,
+
/* block 145 */
-583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,
-583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,
-583,583,583,583,583,583,583,583,115,115,115,115,115,115,115,115,
-584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
-584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
-584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,
-584,584,584,584,115,115,115,115,115,115,115,115,115,115,115,585,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,
+613,613,613,613,613,613,613,613,613,613,613,613,613,613,115,115,
+614,614,614,614,614,614,614,614,614,614,115,115,115,115,115,115,
+615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,
+615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,
+615,615,615,615,115,115,115,115,616,616,616,616,616,616,616,616,
+616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,
+616,616,616,616,616,616,616,616,616,616,616,616,115,115,115,115,
/* block 146 */
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
+617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,
+617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,617,
+617,617,617,617,617,617,617,617,115,115,115,115,115,115,115,115,
+618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,
+618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,
+618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,
+618,618,618,618,115,115,115,115,115,115,115,115,115,115,115,619,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 147 */
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,586,115,115,115,115,115,115,115,115,115,
-586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,
-586,586,586,586,586,586,115,115,115,115,115,115,115,115,115,115,
-586,586,586,586,586,586,586,586,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
/* block 148 */
-587,587,587,587,587,587,115,115,587,115,587,587,587,587,587,587,
-587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
-587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
-587,587,587,587,587,587,115,587,587,115,115,115,587,115,115,587,
-588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,
-588,588,588,588,588,588,115,589,590,590,590,590,590,590,590,590,
-591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
-591,591,591,591,591,591,591,592,592,593,593,593,593,593,593,593,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,620,115,115,115,115,115,115,115,115,115,
+620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
+620,620,620,620,620,620,115,115,115,115,115,115,115,115,115,115,
+620,620,620,620,620,620,620,620,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 149 */
-594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,
-594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,115,
-115,115,115,115,115,115,115,595,595,595,595,595,595,595,595,595,
+621,621,621,621,621,621,115,115,621,115,621,621,621,621,621,621,
+621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,
+621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,
+621,621,621,621,621,621,115,621,621,115,115,115,621,115,115,621,
+622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,
+622,622,622,622,622,622,115,623,624,624,624,624,624,624,624,624,
+625,625,625,625,625,625,625,625,625,625,625,625,625,625,625,625,
+625,625,625,625,625,625,625,626,626,627,627,627,627,627,627,627,
+
+/* block 150 */
+628,628,628,628,628,628,628,628,628,628,628,628,628,628,628,628,
+628,628,628,628,628,628,628,628,628,628,628,628,628,628,628,115,
+115,115,115,115,115,115,115,629,629,629,629,629,629,629,629,629,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,
-596,596,596,115,596,596,115,115,115,115,115,597,597,597,597,597,
+630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,
+630,630,630,115,630,630,115,115,115,115,115,631,631,631,631,631,
-/* block 150 */
-598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,
-598,598,598,598,598,598,599,599,599,599,599,599,115,115,115,600,
-601,601,601,601,601,601,601,601,601,601,601,601,601,601,601,601,
-601,601,601,601,601,601,601,601,601,601,115,115,115,115,115,602,
+/* block 151 */
+632,632,632,632,632,632,632,632,632,632,632,632,632,632,632,632,
+632,632,632,632,632,632,633,633,633,633,633,633,115,115,115,634,
+635,635,635,635,635,635,635,635,635,635,635,635,635,635,635,635,
+635,635,635,635,635,635,635,635,635,635,115,115,115,115,115,636,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 151 */
-603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,
-603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,
-604,604,604,604,604,604,604,604,604,604,604,604,604,604,604,604,
-604,604,604,604,604,604,604,604,115,115,115,115,605,605,604,604,
-605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,
-115,115,605,605,605,605,605,605,605,605,605,605,605,605,605,605,
-605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,
-605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,
-
/* block 152 */
-606,607,607,607,115,607,607,115,115,115,115,115,607,607,607,607,
-606,606,606,606,115,606,606,606,115,606,606,606,606,606,606,606,
-606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,
-606,606,606,606,115,115,115,115,607,607,607,115,115,115,115,607,
-608,608,608,608,608,608,608,608,115,115,115,115,115,115,115,115,
-609,609,609,609,609,609,609,609,609,115,115,115,115,115,115,115,
-610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,
-610,610,610,610,610,610,610,610,610,610,610,610,610,611,611,612,
+637,637,637,637,637,637,637,637,637,637,637,637,637,637,637,637,
+637,637,637,637,637,637,637,637,637,637,637,637,637,637,637,637,
+638,638,638,638,638,638,638,638,638,638,638,638,638,638,638,638,
+638,638,638,638,638,638,638,638,115,115,115,115,639,639,638,638,
+639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,
+115,115,639,639,639,639,639,639,639,639,639,639,639,639,639,639,
+639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,
+639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,639,
/* block 153 */
-613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,
-613,613,613,613,613,613,613,613,613,613,613,613,613,614,614,614,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-615,615,615,615,615,615,615,615,616,615,615,615,615,615,615,615,
-615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,
-615,615,615,615,615,617,617,115,115,115,115,618,618,618,618,618,
-619,619,619,619,619,619,619,115,115,115,115,115,115,115,115,115,
+640,641,641,641,115,641,641,115,115,115,115,115,641,641,641,641,
+640,640,640,640,115,640,640,640,115,640,640,640,640,640,640,640,
+640,640,640,640,640,640,640,640,640,640,640,640,640,640,640,640,
+640,640,640,640,115,115,115,115,641,641,641,115,115,115,115,641,
+642,642,642,642,642,642,642,642,115,115,115,115,115,115,115,115,
+643,643,643,643,643,643,643,643,643,115,115,115,115,115,115,115,
+644,644,644,644,644,644,644,644,644,644,644,644,644,644,644,644,
+644,644,644,644,644,644,644,644,644,644,644,644,644,645,645,646,
/* block 154 */
-620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
-620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
-620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,
-620,620,620,620,620,620,115,115,115,621,621,621,621,621,621,621,
-622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,
-622,622,622,622,622,622,115,115,623,623,623,623,623,623,623,623,
-624,624,624,624,624,624,624,624,624,624,624,624,624,624,624,624,
-624,624,624,115,115,115,115,115,625,625,625,625,625,625,625,625,
+647,647,647,647,647,647,647,647,647,647,647,647,647,647,647,647,
+647,647,647,647,647,647,647,647,647,647,647,647,647,648,648,648,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+649,649,649,649,649,649,649,649,650,649,649,649,649,649,649,649,
+649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,
+649,649,649,649,649,651,651,115,115,115,115,652,652,652,652,652,
+653,653,653,653,653,653,653,115,115,115,115,115,115,115,115,115,
/* block 155 */
-626,626,626,626,626,626,626,626,626,626,626,626,626,626,626,626,
-626,626,115,115,115,115,115,115,115,627,627,627,627,115,115,115,
-115,115,115,115,115,115,115,115,115,628,628,628,628,628,628,628,
+654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,
+654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,
+654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,
+654,654,654,654,654,654,115,115,115,655,655,655,655,655,655,655,
+656,656,656,656,656,656,656,656,656,656,656,656,656,656,656,656,
+656,656,656,656,656,656,115,115,657,657,657,657,657,657,657,657,
+658,658,658,658,658,658,658,658,658,658,658,658,658,658,658,658,
+658,658,658,115,115,115,115,115,659,659,659,659,659,659,659,659,
+
+/* block 156 */
+660,660,660,660,660,660,660,660,660,660,660,660,660,660,660,660,
+660,660,115,115,115,115,115,115,115,661,661,661,661,115,115,115,
+115,115,115,115,115,115,115,115,115,662,662,662,662,662,662,662,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 156 */
-629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,
-629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,
-629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,
-629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,
-629,629,629,629,629,629,629,629,629,115,115,115,115,115,115,115,
+/* block 157 */
+663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,
+663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,
+663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,
+663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,
+663,663,663,663,663,663,663,663,663,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 157 */
-630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,
-630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,
-630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,
-630,630,630,115,115,115,115,115,115,115,115,115,115,115,115,115,
-631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,
-631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,
-631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,
-631,631,631,115,115,115,115,115,115,115,632,632,632,632,632,632,
-
/* block 158 */
+664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,
+664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,
+664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,664,
+664,664,664,115,115,115,115,115,115,115,115,115,115,115,115,115,
+665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,
+665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,
+665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,665,
+665,665,665,115,115,115,115,115,115,115,666,666,666,666,666,666,
+
+/* block 159 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,
-633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,115,
-
-/* block 159 */
-634,635,634,636,636,636,636,636,636,636,636,636,636,636,636,636,
-636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,
-636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,
-636,636,636,636,636,636,636,636,635,635,635,635,635,635,635,635,
-635,635,635,635,635,635,635,637,637,637,637,637,637,637,115,115,
-115,115,638,638,638,638,638,638,638,638,638,638,638,638,638,638,
-638,638,638,638,638,638,639,639,639,639,639,639,639,639,639,639,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,635,
+667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,
+667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,115,
/* block 160 */
-640,640,641,642,642,642,642,642,642,642,642,642,642,642,642,642,
-642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,
-642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,
-641,641,641,640,640,640,640,641,641,640,640,643,643,644,643,643,
-643,643,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-645,645,645,645,645,645,645,645,645,645,645,645,645,645,645,645,
-645,645,645,645,645,645,645,645,645,115,115,115,115,115,115,115,
-646,646,646,646,646,646,646,646,646,646,115,115,115,115,115,115,
+668,669,668,670,670,670,670,670,670,670,670,670,670,670,670,670,
+670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,
+670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,
+670,670,670,670,670,670,670,670,669,669,669,669,669,669,669,669,
+669,669,669,669,669,669,669,671,671,671,671,671,671,671,115,115,
+115,115,672,672,672,672,672,672,672,672,672,672,672,672,672,672,
+672,672,672,672,672,672,673,673,673,673,673,673,673,673,673,673,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,669,
/* block 161 */
-647,647,647,648,648,648,648,648,648,648,648,648,648,648,648,648,
-648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,
-648,648,648,648,648,648,648,647,647,647,647,647,649,647,647,647,
-647,647,647,647,647,115,650,650,650,650,650,650,650,650,650,650,
-651,651,651,651,115,115,115,115,115,115,115,115,115,115,115,115,
-652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,
-652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,
-652,652,652,653,654,654,652,115,115,115,115,115,115,115,115,115,
+674,674,675,676,676,676,676,676,676,676,676,676,676,676,676,676,
+676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,
+676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,
+675,675,675,674,674,674,674,675,675,674,674,677,677,678,677,677,
+677,677,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+679,679,679,679,679,679,679,679,679,679,679,679,679,679,679,679,
+679,679,679,679,679,679,679,679,679,115,115,115,115,115,115,115,
+680,680,680,680,680,680,680,680,680,680,115,115,115,115,115,115,
/* block 162 */
-655,655,656,657,657,657,657,657,657,657,657,657,657,657,657,657,
-657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,
-657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,
-657,657,657,656,656,656,655,655,655,655,655,655,655,655,655,656,
-656,657,657,657,657,658,658,658,658,658,655,655,655,658,115,115,
-659,659,659,659,659,659,659,659,659,659,657,658,657,658,658,658,
-115,660,660,660,660,660,660,660,660,660,660,660,660,660,660,660,
-660,660,660,660,660,115,115,115,115,115,115,115,115,115,115,115,
+681,681,681,682,682,682,682,682,682,682,682,682,682,682,682,682,
+682,682,682,682,682,682,682,682,682,682,682,682,682,682,682,682,
+682,682,682,682,682,682,682,681,681,681,681,681,683,681,681,681,
+681,681,681,681,681,115,684,684,684,684,684,684,684,684,684,684,
+685,685,685,685,115,115,115,115,115,115,115,115,115,115,115,115,
+686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,
+686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,
+686,686,686,687,688,688,686,115,115,115,115,115,115,115,115,115,
/* block 163 */
-661,661,661,661,661,661,661,661,661,661,661,661,661,661,661,661,
-661,661,115,661,661,661,661,661,661,661,661,661,661,661,661,661,
-661,661,661,661,661,661,661,661,661,661,661,661,662,662,662,663,
-663,663,662,662,663,662,663,663,664,664,664,664,664,664,115,115,
+689,689,690,691,691,691,691,691,691,691,691,691,691,691,691,691,
+691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,
+691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,
+691,691,691,690,690,690,689,689,689,689,689,689,689,689,689,690,
+690,691,692,692,691,693,693,693,693,693,689,689,689,693,115,115,
+694,694,694,694,694,694,694,694,694,694,691,693,691,693,693,693,
+115,695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,
+695,695,695,695,695,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 164 */
+696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,
+696,696,115,696,696,696,696,696,696,696,696,696,696,696,696,696,
+696,696,696,696,696,696,696,696,696,696,696,696,697,697,697,698,
+698,698,697,697,698,697,698,698,699,699,699,699,699,699,698,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 164 */
-665,665,665,665,665,665,665,115,665,115,665,665,665,665,115,665,
-665,665,665,665,665,665,665,665,665,665,665,665,665,665,115,665,
-665,665,665,665,665,665,665,665,665,666,115,115,115,115,115,115,
-667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,
-667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,
-667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,668,
-669,669,669,668,668,668,668,668,668,668,668,115,115,115,115,115,
-670,670,670,670,670,670,670,670,670,670,115,115,115,115,115,115,
-
/* block 165 */
-671,671,672,672,115,673,673,673,673,673,673,673,673,115,115,673,
-673,115,115,673,673,673,673,673,673,673,673,673,673,673,673,673,
-673,673,673,673,673,673,673,673,673,115,673,673,673,673,673,673,
-673,115,673,673,115,673,673,673,673,673,115,115,671,673,674,672,
-671,672,672,672,672,115,115,672,672,115,115,672,672,672,115,115,
-673,115,115,115,115,115,115,674,115,115,115,115,115,673,673,673,
-673,673,672,672,115,115,671,671,671,671,671,671,671,115,115,115,
-671,671,671,671,671,115,115,115,115,115,115,115,115,115,115,115,
+700,700,700,700,700,700,700,115,700,115,700,700,700,700,115,700,
+700,700,700,700,700,700,700,700,700,700,700,700,700,700,115,700,
+700,700,700,700,700,700,700,700,700,701,115,115,115,115,115,115,
+702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,
+702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,
+702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,703,
+704,704,704,703,703,703,703,703,703,703,703,115,115,115,115,115,
+705,705,705,705,705,705,705,705,705,705,115,115,115,115,115,115,
/* block 166 */
-675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,
-675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,
-675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,
-676,677,677,678,678,678,678,678,678,677,678,677,677,676,677,678,
-678,677,678,678,675,675,679,675,115,115,115,115,115,115,115,115,
-680,680,680,680,680,680,680,680,680,680,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+706,706,707,707,115,708,708,708,708,708,708,708,708,115,115,708,
+708,115,115,708,708,708,708,708,708,708,708,708,708,708,708,708,
+708,708,708,708,708,708,708,708,708,115,708,708,708,708,708,708,
+708,115,708,708,115,708,708,708,708,708,115,115,706,708,709,707,
+706,707,707,707,707,115,115,707,707,115,115,707,707,707,115,115,
+708,115,115,115,115,115,115,709,115,115,115,115,115,708,708,708,
+708,708,707,707,115,115,706,706,706,706,706,706,706,115,115,115,
+706,706,706,706,706,115,115,115,115,115,115,115,115,115,115,115,
/* block 167 */
-681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,
-681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,
-681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,682,
-683,683,684,684,684,684,115,115,683,683,683,683,684,684,683,684,
-684,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,
-685,685,685,685,685,685,685,685,681,681,681,681,684,684,115,115,
+710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,
+710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,
+710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,
+710,710,710,710,710,711,711,711,712,712,712,712,712,712,712,712,
+711,711,712,712,712,711,712,710,710,710,710,713,713,713,713,713,
+714,714,714,714,714,714,714,714,714,714,115,713,115,713,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 168 */
-686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,
-686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,
-686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,
-687,687,687,688,688,688,688,688,688,688,688,687,687,688,687,688,
-688,689,689,689,686,115,115,115,115,115,115,115,115,115,115,115,
-690,690,690,690,690,690,690,690,690,690,115,115,115,115,115,115,
+715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,
+715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,
+715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,
+716,717,717,718,718,718,718,718,718,717,718,717,717,716,717,718,
+718,717,718,718,715,715,719,715,115,115,115,115,115,115,115,115,
+720,720,720,720,720,720,720,720,720,720,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 169 */
-691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,
-691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,
-691,691,691,691,691,691,691,691,691,691,691,692,693,692,693,693,
-692,692,692,692,692,692,693,692,115,115,115,115,115,115,115,115,
-694,694,694,694,694,694,694,694,694,694,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,
+721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,
+721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,722,
+723,723,724,724,724,724,115,115,723,723,723,723,724,724,723,724,
+724,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,721,721,721,721,724,724,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 170 */
-695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,
-695,695,695,695,695,695,695,695,695,695,115,115,115,696,696,696,
-697,697,696,696,696,696,697,696,696,696,696,696,115,115,115,115,
-698,698,698,698,698,698,698,698,698,698,699,699,700,700,700,701,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
+726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
+726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
+727,727,727,728,728,728,728,728,728,728,728,727,727,728,727,728,
+728,729,729,729,726,115,115,115,115,115,115,115,115,115,115,115,
+730,730,730,730,730,730,730,730,730,730,115,115,115,115,115,115,
+369,369,369,369,369,369,369,369,369,369,369,369,369,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 171 */
+731,731,731,731,731,731,731,731,731,731,731,731,731,731,731,731,
+731,731,731,731,731,731,731,731,731,731,731,731,731,731,731,731,
+731,731,731,731,731,731,731,731,731,731,731,732,733,732,733,733,
+732,732,732,732,732,732,733,732,115,115,115,115,115,115,115,115,
+734,734,734,734,734,734,734,734,734,734,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,
-702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,
-703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,
-703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,
-704,704,704,704,704,704,704,704,704,704,705,705,705,705,705,705,
-705,705,705,115,115,115,115,115,115,115,115,115,115,115,115,706,
/* block 172 */
+735,735,735,735,735,735,735,735,735,735,735,735,735,735,735,735,
+735,735,735,735,735,735,735,735,735,735,115,115,115,736,736,736,
+737,737,736,736,736,736,737,736,736,736,736,736,115,115,115,115,
+738,738,738,738,738,738,738,738,738,738,739,739,740,740,740,741,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
-707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
-707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
-707,707,707,707,707,707,707,707,707,115,115,115,115,115,115,115,
/* block 173 */
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-
-/* block 174 */
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,
+742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,
+743,743,743,743,743,743,743,743,743,743,743,743,743,743,743,743,
+743,743,743,743,743,743,743,743,743,743,743,743,743,743,743,743,
+744,744,744,744,744,744,744,744,744,744,745,745,745,745,745,745,
+745,745,745,115,115,115,115,115,115,115,115,115,115,115,115,746,
+
+/* block 174 */
+747,748,748,748,748,748,748,749,749,748,748,747,747,747,747,747,
+747,747,747,747,747,747,747,747,747,747,747,747,747,747,747,747,
+747,747,747,747,747,747,747,747,747,747,747,747,747,747,747,747,
+747,747,747,748,748,748,748,748,748,749,750,748,748,748,748,751,
+751,751,751,751,751,751,751,748,115,115,115,115,115,115,115,115,
+752,753,753,753,753,753,753,754,754,753,753,753,752,752,752,752,
+752,752,752,752,752,752,752,752,752,752,752,752,752,752,752,752,
+752,752,752,752,752,752,752,752,752,752,752,752,752,752,752,752,
/* block 175 */
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,
-709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,115,
-710,710,710,710,710,115,115,115,115,115,115,115,115,115,115,115,
+752,752,752,752,115,115,755,755,755,755,753,753,753,753,753,753,
+753,753,753,753,753,753,753,754,753,753,756,756,756,115,756,756,
+756,756,756,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,
+757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,
+757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,
+757,757,757,757,757,757,757,757,757,115,115,115,115,115,115,115,
/* block 176 */
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,
-708,708,708,708,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+758,758,758,758,758,758,758,758,758,115,758,758,758,758,758,758,
+758,758,758,758,758,758,758,758,758,758,758,758,758,758,758,758,
+758,758,758,758,758,758,758,758,758,758,758,758,758,758,758,759,
+760,760,760,760,760,760,760,115,760,760,760,760,760,760,759,760,
+758,761,761,761,761,761,115,115,115,115,115,115,115,115,115,115,
+762,762,762,762,762,762,762,762,762,762,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,115,115,115,
+764,764,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
/* block 177 */
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-
-/* block 178 */
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,
-711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,115,
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+115,115,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+766,766,766,766,766,766,766,766,115,767,766,766,766,766,766,766,
+766,767,766,766,767,766,766,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 178 */
+768,768,768,768,768,768,768,115,768,768,115,768,768,768,768,768,
+768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,
+768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,
+768,769,769,769,769,769,769,115,115,115,769,115,769,769,115,769,
+769,769,769,769,769,769,770,769,115,115,115,115,115,115,115,115,
+771,771,771,771,771,771,771,771,771,771,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 179 */
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
/* block 180 */
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,
-712,712,712,712,712,712,712,115,115,115,115,115,115,115,115,115,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 181 */
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,
+773,773,773,773,773,773,773,773,773,773,773,773,773,773,773,115,
+774,774,774,774,774,115,115,115,115,115,115,115,115,115,115,115,
/* block 182 */
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,
-498,498,498,498,498,498,498,498,498,115,115,115,115,115,115,115,
-713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,
-713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,115,
-714,714,714,714,714,714,714,714,714,714,115,115,115,115,715,715,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,
+772,772,772,772,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
/* block 183 */
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+
+/* block 184 */
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,
+775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,
-716,716,716,716,716,716,716,716,716,716,716,716,716,716,115,115,
-717,717,717,717,717,718,115,115,115,115,115,115,115,115,115,115,
-
-/* block 184 */
-719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,
-719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,
-719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,
-720,720,720,720,720,720,720,721,721,721,721,721,722,722,722,722,
-723,723,723,723,721,722,115,115,115,115,115,115,115,115,115,115,
-724,724,724,724,724,724,724,724,724,724,115,725,725,725,725,725,
-725,725,115,719,719,719,719,719,719,719,719,719,719,719,719,719,
-719,719,719,719,719,719,719,719,115,115,115,115,115,719,719,719,
/* block 185 */
-719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+
+/* block 186 */
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,
+776,776,776,776,776,776,776,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 187 */
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+
+/* block 188 */
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,
+530,530,530,530,530,530,530,530,530,115,115,115,115,115,115,115,
+777,777,777,777,777,777,777,777,777,777,777,777,777,777,777,777,
+777,777,777,777,777,777,777,777,777,777,777,777,777,777,777,115,
+778,778,778,778,778,778,778,778,778,778,115,115,115,115,779,779,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 189 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+780,780,780,780,780,780,780,780,780,780,780,780,780,780,780,780,
+780,780,780,780,780,780,780,780,780,780,780,780,780,780,115,115,
+781,781,781,781,781,782,115,115,115,115,115,115,115,115,115,115,
-/* block 186 */
-726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
-726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
-726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
-726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,
-726,726,726,726,726,115,115,115,115,115,115,115,115,115,115,115,
-726,727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,
-727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,
-727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,115,
+/* block 190 */
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+784,784,784,784,784,784,784,785,785,785,785,785,786,786,786,786,
+787,787,787,787,785,786,115,115,115,115,115,115,115,115,115,115,
+788,788,788,788,788,788,788,788,788,788,115,789,789,789,789,789,
+789,789,115,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,115,115,115,115,115,783,783,783,
-/* block 187 */
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,728,
-728,728,728,729,729,729,729,729,729,729,729,729,729,729,729,729,
+/* block 191 */
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
@@ -3257,29 +3396,119 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 188 */
-479,477,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 192 */
+790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,
+790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,
+790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,
+790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,790,
+790,790,790,790,790,115,115,115,115,115,115,115,115,115,115,115,
+790,791,791,791,791,791,791,791,791,791,791,791,791,791,791,791,
+791,791,791,791,791,791,791,791,791,791,791,791,791,791,791,791,
+791,791,791,791,791,791,791,791,791,791,791,791,791,791,791,115,
+
+/* block 193 */
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,792,
+792,792,792,793,793,793,793,793,793,793,793,793,793,793,793,793,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+794,795,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 194 */
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+
+/* block 195 */
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 196 */
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,796,
+796,796,796,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 197 */
+509,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+
+/* block 198 */
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+
+/* block 199 */
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,
+507,507,507,507,507,507,507,507,507,507,507,507,507,507,507,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
-/* block 189 */
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,
-730,730,730,730,730,730,730,730,730,730,730,115,115,115,115,115,
-730,730,730,730,730,730,730,730,730,730,730,730,730,115,115,115,
+/* block 200 */
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
-/* block 190 */
-730,730,730,730,730,730,730,730,730,115,115,115,115,115,115,115,
-730,730,730,730,730,730,730,730,730,730,115,115,731,732,732,733,
+/* block 201 */
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,
+797,797,797,797,797,797,797,797,797,797,797,797,115,115,115,115,
+
+/* block 202 */
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,
+798,798,798,798,798,798,798,798,798,798,798,115,115,115,115,115,
+798,798,798,798,798,798,798,798,798,798,798,798,798,115,115,115,
+
+/* block 203 */
+798,798,798,798,798,798,798,798,798,115,115,115,115,115,115,115,
+798,798,798,798,798,798,798,798,798,798,115,115,799,800,800,801,
22, 22, 22, 22,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
@@ -3287,7 +3516,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 191 */
+/* block 204 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -3297,17 +3526,17 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,
-/* block 192 */
+/* block 205 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19,734,406,110,110,110, 19, 19, 19,406,734,734,
-734,734,734, 22, 22, 22, 22, 22, 22, 22, 22,110,110,110,110,110,
+ 19, 19, 19, 19, 19,802,433,110,110,110, 19, 19, 19,433,802,802,
+802,802,802, 22, 22, 22, 22, 22, 22, 22, 22,110,110,110,110,110,
-/* block 193 */
+/* block 206 */
110,110,110, 19, 19,110,110,110,110,110,110,110, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,110,110,110,110, 19, 19,
@@ -3317,17 +3546,17 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 194 */
-564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,
-564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,
-564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,
-564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,
-564,564,735,735,735,564,115,115,115,115,115,115,115,115,115,115,
+/* block 207 */
+596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,
+596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,
+596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,
+596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,
+596,596,803,803,803,596,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 195 */
+/* block 208 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -3337,157 +3566,177 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 196 */
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,439,439,
-439,439,439,439,439,115,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
+/* block 209 */
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,467,467,
+467,467,467,467,467,115,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
-/* block 197 */
-438,438,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,438,115,438,438,
-115,115,438,115,115,438,438,115,115,438,438,438,438,115,438,438,
-438,438,438,438,438,438,439,439,439,439,115,439,115,439,439,439,
-439,439,439,439,115,439,439,439,439,439,439,439,439,439,439,439,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
+/* block 210 */
+466,466,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,466,115,466,466,
+115,115,466,115,115,466,466,115,115,466,466,466,466,115,466,466,
+466,466,466,466,466,466,467,467,467,467,115,467,115,467,467,467,
+467,467,467,467,115,467,467,467,467,467,467,467,467,467,467,467,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
-/* block 198 */
-439,439,439,439,438,438,115,438,438,438,438,115,115,438,438,438,
-438,438,438,438,438,115,438,438,438,438,438,438,438,115,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,438,438,115,438,438,438,438,115,
-438,438,438,438,438,115,438,115,115,115,438,438,438,438,438,438,
-438,115,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
+/* block 211 */
+467,467,467,467,466,466,115,466,466,466,466,115,115,466,466,466,
+466,466,466,466,466,115,466,466,466,466,466,466,466,115,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,466,466,115,466,466,466,466,115,
+466,466,466,466,466,115,466,115,115,115,466,466,466,466,466,466,
+466,115,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
-/* block 199 */
-438,438,438,438,438,438,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
+/* block 212 */
+466,466,466,466,466,466,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
-/* block 200 */
-439,439,439,439,439,439,439,439,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
+/* block 213 */
+467,467,467,467,467,467,467,467,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
-/* block 201 */
-438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,115,115,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438, 8,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439, 8,439,439,439,439,
-439,439,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438, 8,439,439,439,439,
+/* block 214 */
+466,466,466,466,466,466,466,466,466,466,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,115,115,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466, 8,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467, 8,467,467,467,467,
+467,467,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466, 8,467,467,467,467,
-/* block 202 */
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439, 8,439,439,439,439,439,439,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438, 8,439,439,439,439,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, 8,
-439,439,439,439,439,439,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, 8,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
+/* block 215 */
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467, 8,467,467,467,467,467,467,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466, 8,467,467,467,467,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, 8,
+467,467,467,467,467,467,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466, 8,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
-/* block 203 */
-439,439,439,439,439,439,439,439,439, 8,439,439,439,439,439,439,
-438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
-438,438,438,438,438,438,438,438,438, 8,439,439,439,439,439,439,
-439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,
-439,439,439, 8,439,439,439,439,439,439,438,439,115,115, 10, 10,
+/* block 216 */
+467,467,467,467,467,467,467,467,467, 8,467,467,467,467,467,467,
+466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,
+466,466,466,466,466,466,466,466,466, 8,467,467,467,467,467,467,
+467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,
+467,467,467, 8,467,467,467,467,467,467,466,467,115,115, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-/* block 204 */
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
-736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,
+/* block 217 */
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
+804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,
-/* block 205 */
-737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
-737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
-737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
-737,737,737,737,737,737,737,736,736,736,736,737,737,737,737,737,
-737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
-737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
-737,737,737,737,737,737,737,737,737,737,737,737,737,736,736,736,
-736,736,736,736,736,737,736,736,736,736,736,736,736,736,736,736,
+/* block 218 */
+805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
+805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
+805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
+805,805,805,805,805,805,805,804,804,804,804,805,805,805,805,805,
+805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
+805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
+805,805,805,805,805,805,805,805,805,805,805,805,805,804,804,804,
+804,804,804,804,804,805,804,804,804,804,804,804,804,804,804,804,
-/* block 206 */
-736,736,736,736,737,736,736,738,738,738,738,738,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,737,737,737,737,737,
-115,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,
+/* block 219 */
+804,804,804,804,805,804,804,806,806,806,806,806,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,805,805,805,805,805,
+115,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 207 */
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
+/* block 220 */
+807,807,807,807,807,807,807,115,807,807,807,807,807,807,807,807,
+807,807,807,807,807,807,807,807,807,115,115,807,807,807,807,807,
+807,807,115,807,807,115,807,807,807,807,807,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 208 */
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
-739,739,739,739,739,115,115,740,740,740,740,740,740,740,740,740,
-741,741,741,741,741,741,741,115,115,115,115,115,115,115,115,115,
+/* block 221 */
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+
+/* block 222 */
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,808,
+808,808,808,808,808,115,115,809,809,809,809,809,809,809,809,809,
+810,810,810,810,810,810,810,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 209 */
-200,200,200,200,115,200,200,200,200,200,200,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,
-115,200,200,115,200,115,115,200,115,200,200,200,200,200,200,200,
-200,200,200,115,200,200,200,200,115,200,115,200,115,115,115,115,
-115,115,200,115,115,115,115,200,115,200,115,200,115,200,200,200,
-115,200,200,115,200,115,115,200,115,200,115,200,115,200,115,200,
-115,200,200,115,200,115,115,200,200,200,200,115,200,200,200,200,
-200,200,200,115,200,200,200,200,115,200,200,200,200,115,200,115,
+/* block 223 */
+811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,
+811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,
+811,811,812,812,812,812,812,812,812,812,812,812,812,812,812,812,
+812,812,812,812,812,812,812,812,812,812,812,812,812,812,812,812,
+812,812,812,812,813,813,813,813,813,813,813,115,115,115,115,115,
+814,814,814,814,814,814,814,814,814,814,115,115,115,115,815,815,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 210 */
-200,200,200,200,200,200,200,200,200,200,115,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,115,115,115,115,
-115,200,200,200,115,200,200,200,200,200,115,200,200,200,200,200,
-200,200,200,200,200,200,200,200,200,200,200,200,115,115,115,115,
+/* block 224 */
+216,216,216,216,115,216,216,216,216,216,216,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,
+115,216,216,115,216,115,115,216,115,216,216,216,216,216,216,216,
+216,216,216,115,216,216,216,216,115,216,115,216,115,115,115,115,
+115,115,216,115,115,115,115,216,115,216,115,216,115,216,216,216,
+115,216,216,115,216,115,115,216,115,216,115,216,115,216,115,216,
+115,216,216,115,216,115,115,216,216,216,216,115,216,216,216,216,
+216,216,216,115,216,216,216,216,115,216,216,216,216,115,216,115,
+
+/* block 225 */
+216,216,216,216,216,216,216,216,216,216,115,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,115,115,115,115,
+115,216,216,216,115,216,216,216,216,216,115,216,216,216,216,216,
+216,216,216,216,216,216,216,216,216,216,216,216,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-195,195,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+210,210,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 211 */
+/* block 226 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
@@ -3497,7 +3746,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 212 */
+/* block 227 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,
@@ -3507,7 +3756,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,
-/* block 213 */
+/* block 228 */
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,
@@ -3517,67 +3766,107 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 214 */
+/* block 229 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,742,742,742,742,742,742,742,742,742,742,
-742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,
+115,115,115,115,115,115,816,816,816,816,816,816,816,816,816,816,
+816,816,816,816,816,816,816,816,816,816,816,816,816,816,816,816,
-/* block 215 */
-743, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 230 */
+817, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,
19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 216 */
+/* block 231 */
+ 19, 19, 19, 19, 19, 19, 19, 19,479, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,479, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19,479, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+
+/* block 232 */
+ 19, 19, 19, 19, 19,478, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19,479, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19,479, 19, 19, 19,479, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19,478,478,478, 19, 19,478, 19, 19,478,478,478, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 14, 14, 14, 14, 14,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,479, 19,479, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,818,818,818,818,818,
-/* block 217 */
+/* block 233 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19,478,478, 19, 19,478,478,478,478,478,478,478,478,478,478,
+478, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19,819,819,819,819, 19, 19, 19, 19,478, 19,
+478,478,478,478,478,478,478,478,478, 19, 19, 19,478, 19, 19, 19,
+
+/* block 234 */
+ 19,478,478,478, 19,478,478,478, 19, 19, 19,479, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,478, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,479,479, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115, 19, 19, 19, 19, 19,
-/* block 218 */
+/* block 235 */
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19,479, 19, 19, 19, 19,479, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19,478,478, 19, 19, 19, 19,478, 19, 19, 19, 19, 19,
+
+/* block 236 */
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+478, 19, 19, 19, 19,478,478, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,479, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 219 */
+/* block 237 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19,478,478,478, 19, 19, 19,478,478,478,478,478,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+
+/* block 238 */
+479, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19,479, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19,478, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19,478,478,478, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+478, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,478, 19, 19, 19,
+ 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,
- 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,
-/* block 220 */
+/* block 239 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -3587,7 +3876,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 221 */
+/* block 240 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -3597,7 +3886,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 222 */
+/* block 241 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
@@ -3607,7 +3896,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
-/* block 223 */
+/* block 242 */
19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,
@@ -3617,97 +3906,107 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 224 */
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
- 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 243 */
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19,478,478,478,478,478, 19,478,478,
+ 19, 19, 19, 19, 19, 19,478, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+478,478,478,478,478,478,478,478,478,478, 19, 19, 19,478,478,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-/* block 225 */
- 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+/* block 244 */
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 19,478,478,478,478,478,478,478,478,478,478,478,478,478, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,
115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-
-/* block 226 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-
-/* block 227 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,115,115,115,115,115,115,115,115,115,115,115,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-
-/* block 228 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-
-/* block 229 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-
-/* block 230 */
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,
-485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
-
-/* block 231 */
-437, 22,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
-
-/* block 232 */
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-/* block 233 */
+/* block 245 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 246 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,115,115,115,115,115,115,115,115,115,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+
+/* block 247 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+
+/* block 248 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+
+/* block 249 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 250 */
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,
+515,515,515,515,515,515,515,515,515,515,515,515,515,515,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+
+/* block 251 */
+465, 22,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,820,
+
+/* block 252 */
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+
+/* block 253 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
@@ -3717,7 +4016,7 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
-/* block 234 */
+/* block 254 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
@@ -3725,17 +4024,17 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
-437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,
-
-/* block 235 */
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
-557,557,557,557,557,557,557,557,557,557,557,557,557,557,115,115,
+465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,
+
+/* block 255 */
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,
+589,589,589,589,589,589,589,589,589,589,589,589,589,589,115,115,
};
diff --git a/thirdparty/pcre2/src/pcre2_ucp.h b/thirdparty/pcre2/src/pcre2_ucp.h
index 02e5012c29..defba4c10e 100644
--- a/thirdparty/pcre2/src/pcre2_ucp.h
+++ b/thirdparty/pcre2/src/pcre2_ucp.h
@@ -100,9 +100,7 @@ enum {
ucp_Zs /* Space separator */
};
-/* These are grapheme break properties. Note that the code for processing them
-assumes that the values are less than 16. If more values are added that take
-the number to 16 or more, the code will have to be rewritten. */
+/* These are grapheme break properties. */
enum {
ucp_gbCR, /* 0 */
@@ -117,7 +115,12 @@ enum {
ucp_gbLV, /* 9 Hangul syllable type LV */
ucp_gbLVT, /* 10 Hangul syllable type LVT */
ucp_gbRegionalIndicator, /* 11 */
- ucp_gbOther /* 12 */
+ ucp_gbOther, /* 12 */
+ ucp_gbE_Base, /* 13 */
+ ucp_gbE_Modifier, /* 14 */
+ ucp_gbE_Base_GAZ, /* 15 */
+ ucp_gbZWJ, /* 16 */
+ ucp_gbGlue_After_Zwj /* 17 */
};
/* These are the script identifications. */
@@ -184,13 +187,13 @@ enum {
ucp_Tifinagh,
ucp_Ugaritic,
ucp_Yi,
- /* New for Unicode 5.0: */
+ /* New for Unicode 5.0 */
ucp_Balinese,
ucp_Cuneiform,
ucp_Nko,
ucp_Phags_Pa,
ucp_Phoenician,
- /* New for Unicode 5.1: */
+ /* New for Unicode 5.1 */
ucp_Carian,
ucp_Cham,
ucp_Kayah_Li,
@@ -202,7 +205,7 @@ enum {
ucp_Saurashtra,
ucp_Sundanese,
ucp_Vai,
- /* New for Unicode 5.2: */
+ /* New for Unicode 5.2 */
ucp_Avestan,
ucp_Bamum,
ucp_Egyptian_Hieroglyphs,
@@ -218,11 +221,11 @@ enum {
ucp_Samaritan,
ucp_Tai_Tham,
ucp_Tai_Viet,
- /* New for Unicode 6.0.0: */
+ /* New for Unicode 6.0.0 */
ucp_Batak,
ucp_Brahmi,
ucp_Mandaic,
- /* New for Unicode 6.1.0: */
+ /* New for Unicode 6.1.0 */
ucp_Chakma,
ucp_Meroitic_Cursive,
ucp_Meroitic_Hieroglyphs,
@@ -230,7 +233,7 @@ enum {
ucp_Sharada,
ucp_Sora_Sompeng,
ucp_Takri,
- /* New for Unicode 7.0.0: */
+ /* New for Unicode 7.0.0 */
ucp_Bassa_Vah,
ucp_Caucasian_Albanian,
ucp_Duployan,
@@ -254,13 +257,24 @@ enum {
ucp_Siddham,
ucp_Tirhuta,
ucp_Warang_Citi,
- /* New for Unicode 8.0.0: */
+ /* New for Unicode 8.0.0 */
ucp_Ahom,
ucp_Anatolian_Hieroglyphs,
ucp_Hatran,
ucp_Multani,
ucp_Old_Hungarian,
- ucp_SignWriting
+ ucp_SignWriting,
+ /* New for Unicode 10.0.0 (no update since 8.0.0) */
+ ucp_Adlam,
+ ucp_Bhaiksuki,
+ ucp_Marchen,
+ ucp_Newa,
+ ucp_Osage,
+ ucp_Tangut,
+ ucp_Masaram_Gondi,
+ ucp_Nushu,
+ ucp_Soyombo,
+ ucp_Zanabazar_Square
};
#endif /* PCRE2_UCP_H_IDEMPOTENT_GUARD */
diff --git a/thirdparty/pcre2/src/pcre2_valid_utf.c b/thirdparty/pcre2/src/pcre2_valid_utf.c
index 3e18f1200b..96e8bff993 100644
--- a/thirdparty/pcre2/src/pcre2_valid_utf.c
+++ b/thirdparty/pcre2/src/pcre2_valid_utf.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
Original API code Copyright (c) 1997-2012 University of Cambridge
- New API code Copyright (c) 2016 University of Cambridge
+ New API code Copyright (c) 2016-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -142,20 +142,20 @@ for (p = string; length > 0; p++)
if (c < 0xc0) /* Isolated 10xx xxxx byte */
{
- *erroroffset = (int)(p - string);
+ *erroroffset = (PCRE2_SIZE)(p - string);
return PCRE2_ERROR_UTF8_ERR20;
}
if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */
{
- *erroroffset = (int)(p - string);
+ *erroroffset = (PCRE2_SIZE)(p - string);
return PCRE2_ERROR_UTF8_ERR21;
}
ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */
if (length < ab) /* Missing bytes */
{
- *erroroffset = (int)(p - string);
+ *erroroffset = (PCRE2_SIZE)(p - string);
switch(ab - length)
{
case 1: return PCRE2_ERROR_UTF8_ERR1;
diff --git a/thirdparty/pcre2/src/sljit/sljitConfig.h b/thirdparty/pcre2/src/sljit/sljitConfig.h
index 2e70224da8..d54b5e6f54 100644
--- a/thirdparty/pcre2/src/sljit/sljitConfig.h
+++ b/thirdparty/pcre2/src/sljit/sljitConfig.h
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -108,8 +108,10 @@
/* Force cdecl calling convention even if a better calling
convention (e.g. fastcall) is supported by the C compiler.
- If this option is enabled, C functions without
- SLJIT_CALL can also be called from JIT code. */
+ If this option is disabled (this is the default), functions
+ called from JIT should be defined with SLJIT_FUNC attribute.
+ Standard C functions can still be called by using the
+ SLJIT_CALL_CDECL jump type. */
#ifndef SLJIT_USE_CDECL_CALLING_CONVENTION
/* Disabled by default */
#define SLJIT_USE_CDECL_CALLING_CONVENTION 0
diff --git a/thirdparty/pcre2/src/sljit/sljitConfigInternal.h b/thirdparty/pcre2/src/sljit/sljitConfigInternal.h
index 5d461017e4..e13282c842 100644
--- a/thirdparty/pcre2/src/sljit/sljitConfigInternal.h
+++ b/thirdparty/pcre2/src/sljit/sljitConfigInternal.h
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -60,11 +60,13 @@
a single precision floating point array by index
SLJIT_F64_SHIFT : the shift required to apply when accessing
a double precision floating point array by index
+ SLJIT_PREF_SHIFT_REG : x86 systems prefers ecx for shifting by register
+ the scratch register index of ecx is stored in this variable
SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
Other macros:
- SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_FUNC : calling convention attribute for both calling JIT form C and C calling back from JIT
SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
*/
@@ -296,6 +298,13 @@
#define SLJIT_CACHE_FLUSH(from, to) \
sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+#define SLJIT_CACHE_FLUSH_OWN_IMPL 1
+
#elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
#define SLJIT_CACHE_FLUSH(from, to) \
@@ -308,13 +317,6 @@
#define SLJIT_CACHE_FLUSH(from, to) \
cacheflush((long)(from), (long)(to), 0)
-#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
-
-/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
-#define SLJIT_CACHE_FLUSH(from, to) \
- ppc_cache_flush((from), (to))
-#define SLJIT_CACHE_FLUSH_OWN_IMPL 1
-
#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
@@ -393,7 +395,9 @@ typedef double sljit_f64;
#ifndef SLJIT_W
/* Defining long constants. */
-#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#define SLJIT_W(w) (w##l)
+#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
#define SLJIT_W(w) (w##ll)
#else
#define SLJIT_W(w) (w)
@@ -469,44 +473,44 @@ typedef double sljit_f64;
/* Calling convention of functions generated by SLJIT or called from the generated code. */
/*****************************************************************************************/
-#ifndef SLJIT_CALL
+#ifndef SLJIT_FUNC
#if (defined SLJIT_USE_CDECL_CALLING_CONVENTION && SLJIT_USE_CDECL_CALLING_CONVENTION)
/* Force cdecl. */
-#define SLJIT_CALL
+#define SLJIT_FUNC
#elif (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
#if defined(__GNUC__) && !defined(__APPLE__)
-#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_FUNC __attribute__ ((fastcall))
#define SLJIT_X86_32_FASTCALL 1
#elif defined(_MSC_VER)
-#define SLJIT_CALL __fastcall
+#define SLJIT_FUNC __fastcall
#define SLJIT_X86_32_FASTCALL 1
#elif defined(__BORLANDC__)
-#define SLJIT_CALL __msfastcall
+#define SLJIT_FUNC __msfastcall
#define SLJIT_X86_32_FASTCALL 1
#else /* Unknown compiler. */
/* The cdecl attribute is the default. */
-#define SLJIT_CALL
+#define SLJIT_FUNC
#endif
#else /* Non x86-32 architectures. */
-#define SLJIT_CALL
+#define SLJIT_FUNC
#endif /* SLJIT_CONFIG_X86_32 */
-#endif /* !SLJIT_CALL */
+#endif /* !SLJIT_FUNC */
#ifndef SLJIT_INDIRECT_CALL
#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
@@ -553,48 +557,44 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
-#define SLJIT_NUMBER_OF_REGISTERS 10
-#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
-#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
-#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
-#else
-/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
-#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
-#endif /* SLJIT_X86_32_FASTCALL */
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 9
+#define SLJIT_LOCALS_OFFSET_BASE (compiler->locals_offset)
+#define SLJIT_PREF_SHIFT_REG SLJIT_R2
#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_NUMBER_OF_REGISTERS 13
#ifndef _WIN64
-#define SLJIT_NUMBER_OF_REGISTERS 12
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
-#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
-#else
-#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#else /* _WIN64 */
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
-#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
-#endif /* _WIN64 */
+#define SLJIT_LOCALS_OFFSET_BASE (compiler->locals_offset)
+#endif /* !_WIN64 */
+#define SLJIT_PREF_SHIFT_REG SLJIT_R3
#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
-#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_REGISTERS 12
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
#define SLJIT_LOCALS_OFFSET_BASE 0
#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
-#define SLJIT_NUMBER_OF_REGISTERS 11
-#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
#define SLJIT_LOCALS_OFFSET_BASE 0
#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
-#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_REGISTERS 26
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
-#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_REGISTERS 23
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
@@ -607,7 +607,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
-#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_REGISTERS 21
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
@@ -620,8 +620,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#define SLJIT_NUMBER_OF_REGISTERS 18
#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
-/* Add +1 for double alignment. */
-#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+/* saved registers (16), return struct pointer (1), space for 6 argument words (1),
+ 4th double arg (2), double alignment (1). */
+#define SLJIT_LOCALS_OFFSET_BASE ((16 + 1 + 6 + 2 + 1) * sizeof(sljit_sw))
#endif
#elif (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
@@ -663,7 +664,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
-#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)
/* SLJIT_HALT_PROCESS must halt the process. */
#ifndef SLJIT_HALT_PROCESS
@@ -675,7 +676,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#include <stdio.h>
-#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+#endif /* !SLJIT_ASSERT || !SLJIT_UNREACHABLE */
/* Feel free to redefine these two macros. */
#ifndef SLJIT_ASSERT
@@ -690,34 +691,33 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
#endif /* !SLJIT_ASSERT */
-#ifndef SLJIT_ASSERT_STOP
+#ifndef SLJIT_UNREACHABLE
-#define SLJIT_ASSERT_STOP() \
+#define SLJIT_UNREACHABLE() \
do { \
printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
SLJIT_HALT_PROCESS(); \
} while (0)
-#endif /* !SLJIT_ASSERT_STOP */
+#endif /* !SLJIT_UNREACHABLE */
#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
/* Forcing empty, but valid statements. */
#undef SLJIT_ASSERT
-#undef SLJIT_ASSERT_STOP
+#undef SLJIT_UNREACHABLE
#define SLJIT_ASSERT(x) \
do { } while (0)
-#define SLJIT_ASSERT_STOP() \
+#define SLJIT_UNREACHABLE() \
do { } while (0)
#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
#ifndef SLJIT_COMPILE_ASSERT
-/* Should be improved eventually. */
#define SLJIT_COMPILE_ASSERT(x, description) \
- SLJIT_ASSERT(x)
+ switch(0) { case 0: case ((x) ? 1 : 0): break; }
#endif /* !SLJIT_COMPILE_ASSERT */
diff --git a/thirdparty/pcre2/src/sljit/sljitExecAllocator.c b/thirdparty/pcre2/src/sljit/sljitExecAllocator.c
index 9f88f990b0..f5009788f6 100644
--- a/thirdparty/pcre2/src/sljit/sljitExecAllocator.c
+++ b/thirdparty/pcre2/src/sljit/sljitExecAllocator.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
diff --git a/thirdparty/pcre2/src/sljit/sljitLir.c b/thirdparty/pcre2/src/sljit/sljitLir.c
index 0b39ec90a9..5e435f0154 100644
--- a/thirdparty/pcre2/src/sljit/sljitLir.c
+++ b/thirdparty/pcre2/src/sljit/sljitLir.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -84,17 +84,26 @@
#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#define VARIABLE_FLAG_SHIFT (10)
+#define VARIABLE_FLAG_MASK (0x3f << VARIABLE_FLAG_SHIFT)
+#define GET_FLAG_TYPE(op) ((op) >> VARIABLE_FLAG_SHIFT)
+
#define GET_OPCODE(op) \
- ((op) & ~(SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))
+ ((op) & ~(SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
-#define GET_FLAGS(op) \
- ((op) & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C))
+#define HAS_FLAGS(op) \
+ ((op) & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))
#define GET_ALL_FLAGS(op) \
- ((op) & (SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))
+ ((op) & (SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define TYPE_CAST_NEEDED(op) \
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S32)
+#else
#define TYPE_CAST_NEEDED(op) \
- (((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16) || ((op) >= SLJIT_MOVU_U8 && (op) <= SLJIT_MOVU_S16))
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16)
+#endif
#define BUF_SIZE 4096
@@ -114,16 +123,19 @@
/* When reg can be unused. */
#define SLOW_IS_REG(reg) ((reg) > 0 && (reg) <= REG_MASK)
+/* Mask for argument types. */
+#define SLJIT_DEF_MASK ((1 << SLJIT_DEF_SHIFT) - 1)
+
/* Jump flags. */
#define JUMP_LABEL 0x1
#define JUMP_ADDR 0x2
/* SLJIT_REWRITABLE_JUMP is 0x1000. */
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
-# define PATCH_MB 0x4
-# define PATCH_MW 0x8
+# define PATCH_MB 0x4
+# define PATCH_MW 0x8
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
-# define PATCH_MD 0x10
+# define PATCH_MD 0x10
#endif
#endif
@@ -338,7 +350,7 @@
/* Public functions */
/* --------------------------------------------------------------------- */
-#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
#define SLJIT_NEEDS_COMPILER_INIT 1
static sljit_s32 compiler_initialized = 0;
/* A thread safe initialization. */
@@ -365,6 +377,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allo
int_op_and_single_op_must_be_the_same);
SLJIT_COMPILE_ASSERT(SLJIT_REWRITABLE_JUMP != SLJIT_F32_OP,
rewritable_jump_and_single_op_must_not_be_the_same);
+ SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_EQUAL_F64 & 0x1) && !(SLJIT_JUMP & 0x1),
+ conditional_flags_must_be_even_numbers);
/* Only the non-zero members must be set. */
compiler->error = SLJIT_SUCCESS;
@@ -499,6 +513,18 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw
}
}
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ if ((current_flags & ~(VARIABLE_FLAG_MASK | SLJIT_I32_OP | SLJIT_SET_Z)) == 0) {
+ compiler->last_flags = GET_FLAG_TYPE(current_flags) | (current_flags & (SLJIT_I32_OP | SLJIT_SET_Z));
+ }
+#endif
+}
+
/* --------------------------------------------------------------------- */
/* Private functions */
/* --------------------------------------------------------------------- */
@@ -573,6 +599,19 @@ static SLJIT_INLINE void reverse_buf(struct sljit_compiler *compiler)
compiler->buf = prev;
}
+static SLJIT_INLINE sljit_s32 get_arg_count(sljit_s32 arg_types)
+{
+ sljit_s32 arg_count = 0;
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ while (arg_types) {
+ arg_count++;
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return arg_count;
+}
+
static SLJIT_INLINE void set_emit_enter(struct sljit_compiler *compiler,
sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
@@ -644,150 +683,109 @@ static SLJIT_INLINE void set_const(struct sljit_const *const_, struct sljit_comp
(((exp) & SLJIT_MEM) && (((exp) & REG_MASK) == reg || OFFS_REG(exp) == reg))
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
-#define FUNCTION_CHECK_OP() \
- CHECK_ARGUMENT(!GET_FLAGS(op) || !(op & SLJIT_KEEP_FLAGS)); \
- switch (GET_OPCODE(op)) { \
- case SLJIT_NOT: \
- case SLJIT_CLZ: \
- case SLJIT_AND: \
- case SLJIT_OR: \
- case SLJIT_XOR: \
- case SLJIT_SHL: \
- case SLJIT_LSHR: \
- case SLJIT_ASHR: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C))); \
- break; \
- case SLJIT_NEG: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))); \
- break; \
- case SLJIT_MUL: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))); \
- break; \
- case SLJIT_ADD: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S))); \
- break; \
- case SLJIT_SUB: \
- break; \
- case SLJIT_ADDC: \
- case SLJIT_SUBC: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O))); \
- break; \
- case SLJIT_BREAKPOINT: \
- case SLJIT_NOP: \
- case SLJIT_LMUL_UW: \
- case SLJIT_LMUL_SW: \
- case SLJIT_MOV: \
- case SLJIT_MOV_U32: \
- case SLJIT_MOV_P: \
- case SLJIT_MOVU: \
- case SLJIT_MOVU_U32: \
- case SLJIT_MOVU_P: \
- /* Nothing allowed */ \
- CHECK_ARGUMENT(!(op & (SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \
- break; \
- default: \
- /* Only SLJIT_I32_OP or SLJIT_F32_OP is allowed. */ \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \
- break; \
- }
-
-#define FUNCTION_CHECK_FOP() \
- CHECK_ARGUMENT(!GET_FLAGS(op) || !(op & SLJIT_KEEP_FLAGS)); \
- switch (GET_OPCODE(op)) { \
- case SLJIT_CMP_F64: \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \
- CHECK_ARGUMENT((op & (SLJIT_SET_E | SLJIT_SET_S))); \
- break; \
- default: \
- /* Only SLJIT_I32_OP or SLJIT_F32_OP is allowed. */ \
- CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \
- break; \
- }
#define FUNCTION_CHECK_IS_REG(r) \
- (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) || \
- ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0))
+ (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) \
+ || ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0))
-#define FUNCTION_CHECK_IS_REG_OR_UNUSED(r) \
- ((r) == SLJIT_UNUSED || \
- ((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) || \
- ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0))
+#define FUNCTION_CHECK_IS_FREG(fr) \
+ (((fr) >= SLJIT_FR0 && (fr) < (SLJIT_FR0 + compiler->fscratches)) \
+ || ((fr) > (SLJIT_FS0 - compiler->fsaveds) && (fr) <= SLJIT_FS0))
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
-#define CHECK_NOT_VIRTUAL_REGISTER(p) \
- CHECK_ARGUMENT((p) < SLJIT_R3 || (p) > SLJIT_R6);
+#define CHECK_IF_VIRTUAL_REGISTER(p) ((p) <= SLJIT_S3 && (p) >= SLJIT_S8)
#else
-#define CHECK_NOT_VIRTUAL_REGISTER(p)
+#define CHECK_IF_VIRTUAL_REGISTER(p) 0
#endif
-#define FUNCTION_CHECK_SRC(p, i) \
- CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); \
- if (FUNCTION_CHECK_IS_REG(p)) \
- CHECK_ARGUMENT((i) == 0); \
- else if ((p) == SLJIT_IMM) \
- ; \
- else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \
- CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \
- else { \
- CHECK_ARGUMENT((p) & SLJIT_MEM); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \
- CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \
- if ((p) & OFFS_REG_MASK) { \
- CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \
- CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \
- CHECK_ARGUMENT(!((i) & ~0x3)); \
- } \
- CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \
- }
+static sljit_s32 function_check_src_mem(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
-#define FUNCTION_CHECK_DST(p, i) \
- CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); \
- if (FUNCTION_CHECK_IS_REG_OR_UNUSED(p)) \
- CHECK_ARGUMENT((i) == 0); \
- else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \
- CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \
- else { \
- CHECK_ARGUMENT((p) & SLJIT_MEM); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \
- CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \
- if ((p) & OFFS_REG_MASK) { \
- CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \
- CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \
- CHECK_ARGUMENT(!((i) & ~0x3)); \
- } \
- CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \
- }
+ if (!(p & SLJIT_MEM))
+ return 0;
-#define FUNCTION_FCHECK(p, i) \
- CHECK_ARGUMENT(compiler->fscratches != -1 && compiler->fsaveds != -1); \
- if (((p) >= SLJIT_FR0 && (p) < (SLJIT_FR0 + compiler->fscratches)) || \
- ((p) > (SLJIT_FS0 - compiler->fsaveds) && (p) <= SLJIT_FS0)) \
- CHECK_ARGUMENT(i == 0); \
- else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \
- CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \
- else { \
- CHECK_ARGUMENT((p) & SLJIT_MEM); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \
- CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \
- if ((p) & OFFS_REG_MASK) { \
- CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \
- CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \
- CHECK_ARGUMENT(((p) & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_SP) && !(i & ~0x3)); \
- } \
- CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \
- }
+ if (!((p & REG_MASK) == SLJIT_UNUSED || FUNCTION_CHECK_IS_REG(p & REG_MASK)))
+ return 0;
+
+ if (CHECK_IF_VIRTUAL_REGISTER(p & REG_MASK))
+ return 0;
+
+ if (p & OFFS_REG_MASK) {
+ if ((p & REG_MASK) == SLJIT_UNUSED)
+ return 0;
+
+ if (!(FUNCTION_CHECK_IS_REG(OFFS_REG(p))))
+ return 0;
-#define FUNCTION_CHECK_OP1() \
- if (GET_OPCODE(op) >= SLJIT_MOVU && GET_OPCODE(op) <= SLJIT_MOVU_P) { \
- CHECK_ARGUMENT(!(src & SLJIT_MEM) || (src & REG_MASK) != SLJIT_SP); \
- CHECK_ARGUMENT(!(dst & SLJIT_MEM) || (dst & REG_MASK) != SLJIT_SP); \
- if ((src & SLJIT_MEM) && (src & REG_MASK)) \
- CHECK_ARGUMENT((dst & REG_MASK) != (src & REG_MASK) && OFFS_REG(dst) != (src & REG_MASK)); \
+ if (CHECK_IF_VIRTUAL_REGISTER(OFFS_REG(p)))
+ return 0;
+
+ if ((i & ~0x3) != 0)
+ return 0;
}
+ return (p & ~(SLJIT_MEM | REG_MASK | OFFS_REG_MASK)) == 0;
+}
+
+#define FUNCTION_CHECK_SRC_MEM(p, i) \
+ CHECK_ARGUMENT(function_check_src_mem(compiler, p, i));
+
+static sljit_s32 function_check_src(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p))
+ return (i == 0);
+
+ if (p == SLJIT_IMM)
+ return 1;
+
+ if (p == SLJIT_MEM1(SLJIT_SP))
+ return (i >= 0 && i < compiler->logical_local_size);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_SRC(p, i) \
+ CHECK_ARGUMENT(function_check_src(compiler, p, i));
+
+static sljit_s32 function_check_dst(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i, sljit_s32 unused)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p) || ((unused) && (p) == SLJIT_UNUSED))
+ return (i == 0);
+
+ if (p == SLJIT_MEM1(SLJIT_SP))
+ return (i >= 0 && i < compiler->logical_local_size);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_DST(p, i, unused) \
+ CHECK_ARGUMENT(function_check_dst(compiler, p, i, unused));
+
+static sljit_s32 function_fcheck(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_FREG(p))
+ return (i == 0);
+
+ if (p == SLJIT_MEM1(SLJIT_SP))
+ return (i >= 0 && i < compiler->logical_local_size);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_FCHECK(p, i) \
+ CHECK_ARGUMENT(function_fcheck(compiler, p, i));
+
#endif /* SLJIT_ARGUMENT_CHECKS */
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
@@ -807,62 +805,72 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp
# define SLJIT_PRINT_D ""
#endif
-#define sljit_verbose_reg(compiler, r) \
- do { \
- if ((r) < (SLJIT_R0 + compiler->scratches)) \
- fprintf(compiler->verbose, "r%d", (r) - SLJIT_R0); \
- else \
- fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - (r)); \
- } while (0)
+static void sljit_verbose_reg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_R0 + compiler->scratches))
+ fprintf(compiler->verbose, "r%d", r - SLJIT_R0);
+ else if (r != SLJIT_SP)
+ fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - r);
+ else
+ fprintf(compiler->verbose, "sp");
+}
-#define sljit_verbose_param(compiler, p, i) \
- if ((p) & SLJIT_IMM) \
- fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i)); \
- else if ((p) & SLJIT_MEM) { \
- if ((p) & REG_MASK) { \
- fputc('[', compiler->verbose); \
- sljit_verbose_reg(compiler, (p) & REG_MASK); \
- if ((p) & OFFS_REG_MASK) { \
- fprintf(compiler->verbose, " + "); \
- sljit_verbose_reg(compiler, OFFS_REG(p)); \
- if (i) \
- fprintf(compiler->verbose, " * %d", 1 << (i)); \
- } \
- else if (i) \
- fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i)); \
- fputc(']', compiler->verbose); \
- } \
- else \
- fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); \
- } else if (p) \
- sljit_verbose_reg(compiler, p); \
- else \
+static void sljit_verbose_freg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_FR0 + compiler->fscratches))
+ fprintf(compiler->verbose, "fr%d", r - SLJIT_FR0);
+ else
+ fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - r);
+}
+
+static void sljit_verbose_param(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_IMM)
+ fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i));
+ else if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, " * %d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
+ } else if (p)
+ sljit_verbose_reg(compiler, p);
+ else
fprintf(compiler->verbose, "unused");
+}
-#define sljit_verbose_fparam(compiler, p, i) \
- if ((p) & SLJIT_MEM) { \
- if ((p) & REG_MASK) { \
- fputc('[', compiler->verbose); \
- sljit_verbose_reg(compiler, (p) & REG_MASK); \
- if ((p) & OFFS_REG_MASK) { \
- fprintf(compiler->verbose, " + "); \
- sljit_verbose_reg(compiler, OFFS_REG(p)); \
- if (i) \
- fprintf(compiler->verbose, "%d", 1 << (i)); \
- } \
- else if (i) \
- fprintf(compiler->verbose, "%" SLJIT_PRINT_D "d", (i)); \
- fputc(']', compiler->verbose); \
- } \
- else \
- fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); \
- } \
- else { \
- if ((p) < (SLJIT_FR0 + compiler->fscratches)) \
- fprintf(compiler->verbose, "fr%d", (p) - SLJIT_FR0); \
- else \
- fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - (p)); \
+static void sljit_verbose_fparam(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, "%d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
}
+ else
+ sljit_verbose_freg(compiler, p);
+}
static const char* op0_names[] = {
(char*)"breakpoint", (char*)"nop", (char*)"lmul.uw", (char*)"lmul.sw",
@@ -905,12 +913,17 @@ static char* jump_names[] = {
(char*)"sig_greater", (char*)"sig_less_equal",
(char*)"overflow", (char*)"not_overflow",
(char*)"mul_overflow", (char*)"mul_not_overflow",
+ (char*)"carry", (char*)"",
(char*)"equal", (char*)"not_equal",
(char*)"less", (char*)"greater_equal",
(char*)"greater", (char*)"less_equal",
(char*)"unordered", (char*)"ordered",
(char*)"jump", (char*)"fast_call",
- (char*)"call0", (char*)"call1", (char*)"call2", (char*)"call3"
+ (char*)"call", (char*)"call.cdecl"
+};
+
+static char* call_arg_names[] = {
+ (char*)"void", (char*)"sw", (char*)"uw", (char*)"s32", (char*)"u32", (char*)"f32", (char*)"f64"
};
#endif /* SLJIT_VERBOSE */
@@ -943,56 +956,104 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_com
}
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 types, arg_count, curr_type;
+#endif
+
SLJIT_UNUSED_ARG(compiler);
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(!(options & ~SLJIT_DOUBLE_ALIGNMENT));
- CHECK_ARGUMENT(args >= 0 && args <= 3);
+ CHECK_ARGUMENT(!(options & ~SLJIT_F64_ALIGNMENT));
CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_REGISTERS);
CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
- CHECK_ARGUMENT(args <= saveds);
CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+ CHECK_ARGUMENT((arg_types & SLJIT_DEF_MASK) == 0);
+
+ types = (arg_types >> SLJIT_DEF_SHIFT);
+ arg_count = 0;
+ while (types != 0 && arg_count < 3) {
+ curr_type = (types & SLJIT_DEF_MASK);
+ CHECK_ARGUMENT(curr_type == SLJIT_ARG_TYPE_SW || curr_type == SLJIT_ARG_TYPE_UW);
+ arg_count++;
+ types >>= SLJIT_DEF_SHIFT;
+ }
+ CHECK_ARGUMENT(arg_count <= saveds && types == 0);
+
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
- if (SLJIT_UNLIKELY(!!compiler->verbose))
- fprintf(compiler->verbose, " enter options:none args:%d scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n",
- args, scratches, saveds, fscratches, fsaveds, local_size);
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " enter options:%s args[", (options & SLJIT_F64_ALIGNMENT) ? "f64_align" : "");
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ while (arg_types) {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ }
+
+ fprintf(compiler->verbose, "] scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
#endif
CHECK_RETURN_OK;
}
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- if (SLJIT_UNLIKELY(compiler->skip_checks)) {
- compiler->skip_checks = 0;
- CHECK_RETURN_OK;
- }
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 types, arg_count, curr_type;
+#endif
+
+ SLJIT_UNUSED_ARG(compiler);
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(!(options & ~SLJIT_DOUBLE_ALIGNMENT));
- CHECK_ARGUMENT(args >= 0 && args <= 3);
+ CHECK_ARGUMENT(!(options & ~SLJIT_F64_ALIGNMENT));
CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_REGISTERS);
CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
- CHECK_ARGUMENT(args <= saveds);
CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+
+ types = (arg_types >> SLJIT_DEF_SHIFT);
+ arg_count = 0;
+ while (types != 0 && arg_count < 3) {
+ curr_type = (types & SLJIT_DEF_MASK);
+ CHECK_ARGUMENT(curr_type == SLJIT_ARG_TYPE_SW || curr_type == SLJIT_ARG_TYPE_UW);
+ arg_count++;
+ types >>= SLJIT_DEF_SHIFT;
+ }
+ CHECK_ARGUMENT(arg_count <= saveds && types == 0);
+
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
- if (SLJIT_UNLIKELY(!!compiler->verbose))
- fprintf(compiler->verbose, " set_context options:none args:%d scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n",
- args, scratches, saveds, fscratches, fsaveds, local_size);
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " set_context options:%s args[", (options & SLJIT_F64_ALIGNMENT) ? "f64_align" : "");
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ while (arg_types) {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ }
+
+ fprintf(compiler->verbose, "] scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
#endif
CHECK_RETURN_OK;
}
@@ -1007,6 +1068,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compi
}
else
CHECK_ARGUMENT(src == 0 && srcw == 0);
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1025,7 +1087,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compi
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
{
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_DST(dst, dstw, 0);
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1041,6 +1104,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_return(struct sljit_
{
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
FUNCTION_CHECK_SRC(src, srcw);
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1058,6 +1123,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op0(struct sljit_compiler
CHECK_ARGUMENT((op >= SLJIT_BREAKPOINT && op <= SLJIT_LMUL_SW)
|| ((op & ~SLJIT_I32_OP) >= SLJIT_DIVMOD_UW && (op & ~SLJIT_I32_OP) <= SLJIT_DIV_SW));
CHECK_ARGUMENT(op < SLJIT_LMUL_UW || compiler->scratches >= 2);
+ if (op >= SLJIT_LMUL_UW)
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose))
@@ -1083,23 +1150,48 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CLZ);
- FUNCTION_CHECK_OP();
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_NOT:
+ /* Only SLJIT_I32_OP and SLJIT_SET_Z are allowed. */
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_NEG:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_P:
+ /* Nothing allowed */
+ CHECK_ARGUMENT(!(op & (SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ default:
+ /* Only SLJIT_I32_OP is allowed. */
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ }
+
+ FUNCTION_CHECK_DST(dst, dstw, 1);
FUNCTION_CHECK_SRC(src, srcw);
- FUNCTION_CHECK_DST(dst, dstw);
- FUNCTION_CHECK_OP1();
+
+ if (GET_OPCODE(op) >= SLJIT_NOT) {
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z));
+ }
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
- if (GET_OPCODE(op) <= SLJIT_MOVU_P)
+ if (GET_OPCODE(op) <= SLJIT_MOV_P)
{
- fprintf(compiler->verbose, " mov%s%s%s ", (GET_OPCODE(op) >= SLJIT_MOVU) ? "u" : "",
- !(op & SLJIT_I32_OP) ? "" : "32", (op != SLJIT_MOV32 && op != SLJIT_MOVU32) ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : "");
+ fprintf(compiler->verbose, " mov%s%s ", !(op & SLJIT_I32_OP) ? "" : "32",
+ (op != SLJIT_MOV32) ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : "");
}
else
{
- fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_I32_OP) ? "" : "32",
- !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_SET_U) ? "" : ".u", !(op & SLJIT_SET_S) ? "" : ".s",
- !(op & SLJIT_SET_O) ? "" : ".o", !(op & SLJIT_SET_C) ? "" : ".c", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k");
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_I32_OP) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
}
sljit_verbose_param(compiler, dst, dstw);
@@ -1123,16 +1215,53 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ASHR);
- FUNCTION_CHECK_OP();
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ case SLJIT_SHL:
+ case SLJIT_LSHR:
+ case SLJIT_ASHR:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_MUL:
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == SLJIT_MUL_OVERFLOW);
+ break;
+ case SLJIT_ADD:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_SUB:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_OVERFLOW)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ break;
+ case SLJIT_ADDC:
+ case SLJIT_SUBC:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((compiler->last_flags & 0xff) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((op & SLJIT_I32_OP) == (compiler->last_flags & SLJIT_I32_OP));
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ FUNCTION_CHECK_DST(dst, dstw, 1);
FUNCTION_CHECK_SRC(src1, src1w);
FUNCTION_CHECK_SRC(src2, src2w);
- FUNCTION_CHECK_DST(dst, dstw);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z));
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
- fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_I32_OP) ? "" : "32",
- !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_SET_U) ? "" : ".u", !(op & SLJIT_SET_S) ? "" : ".s",
- !(op & SLJIT_SET_O) ? "" : ".o", !(op & SLJIT_SET_C) ? "" : ".c", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k");
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_I32_OP) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
sljit_verbose_param(compiler, dst, dstw);
fprintf(compiler->verbose, ", ");
sljit_verbose_param(compiler, src1, src1w);
@@ -1173,6 +1302,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_co
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
CHECK_ARGUMENT(instruction);
+
#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
CHECK_ARGUMENT(size > 0 && size < 16);
#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
@@ -1182,6 +1312,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_co
CHECK_ARGUMENT(size == 4 && (((sljit_sw)instruction) & 0x3) == 0);
#endif
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1204,9 +1335,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1(struct sljit_compile
}
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV_F64 && GET_OPCODE(op) <= SLJIT_ABS_F64);
- FUNCTION_CHECK_FOP();
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
FUNCTION_FCHECK(src, srcw);
FUNCTION_FCHECK(dst, dstw);
#endif
@@ -1232,22 +1363,31 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_com
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z));
+#endif
+
if (SLJIT_UNLIKELY(compiler->skip_checks)) {
compiler->skip_checks = 0;
CHECK_RETURN_OK;
}
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_CMP_F64);
- FUNCTION_CHECK_FOP();
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT((op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_EQUAL_F64 && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_F64));
FUNCTION_FCHECK(src1, src1w);
FUNCTION_FCHECK(src2, src2w);
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
- fprintf(compiler->verbose, " %s%s%s%s ", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_F32_OP) ? ".f32" : ".f64",
- (op & SLJIT_SET_E) ? ".e" : "", (op & SLJIT_SET_S) ? ".s" : "");
+ fprintf(compiler->verbose, " %s%s", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_F32_OP) ? ".f32" : ".f64");
+ if (op & VARIABLE_FLAG_MASK) {
+ fprintf(compiler->verbose, ".%s_f", jump_names[GET_FLAG_TYPE(op)]);
+ }
+ fprintf(compiler->verbose, " ");
sljit_verbose_fparam(compiler, src1, src1w);
fprintf(compiler->verbose, ", ");
sljit_verbose_fparam(compiler, src2, src2w);
@@ -1267,11 +1407,11 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_sw_from_f64(str
}
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CONV_S32_FROM_F64);
- FUNCTION_CHECK_FOP();
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
FUNCTION_FCHECK(src, srcw);
- FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_DST(dst, dstw, 0);
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1297,9 +1437,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_f64_from_sw(str
}
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_SW && GET_OPCODE(op) <= SLJIT_CONV_F64_FROM_S32);
- FUNCTION_CHECK_FOP();
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
FUNCTION_CHECK_SRC(src, srcw);
FUNCTION_FCHECK(dst, dstw);
#endif
@@ -1323,9 +1463,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop2(struct sljit_compile
sljit_s32 src2, sljit_sw src2w)
{
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD_F64 && GET_OPCODE(op) <= SLJIT_DIV_F64);
- FUNCTION_CHECK_FOP();
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
FUNCTION_FCHECK(src1, src1w);
FUNCTION_FCHECK(src2, src2w);
FUNCTION_FCHECK(dst, dstw);
@@ -1348,6 +1488,15 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compil
{
SLJIT_UNUSED_ARG(compiler);
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = 0;
+#endif
+
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose))
fprintf(compiler->verbose, "label:\n");
@@ -1364,9 +1513,19 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_I32_OP)));
- CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_CALL3);
+ CHECK_ARGUMENT((type & 0xff) != GET_FLAG_TYPE(SLJIT_SET_CARRY) && (type & 0xff) != (GET_FLAG_TYPE(SLJIT_SET_CARRY) + 1));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_FAST_CALL);
CHECK_ARGUMENT((type & 0xff) < SLJIT_JUMP || !(type & SLJIT_I32_OP));
- CHECK_ARGUMENT((type & 0xff) <= SLJIT_CALL0 || ((type & 0xff) - SLJIT_CALL0) <= compiler->scratches);
+
+ if ((type & 0xff) < SLJIT_JUMP) {
+ if ((type & 0xff) <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff)
+ || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW));
+ CHECK_ARGUMENT((type & SLJIT_I32_OP) == (compiler->last_flags & SLJIT_I32_OP));
+ }
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose))
@@ -1376,6 +1535,63 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile
CHECK_RETURN_OK;
}
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 i, types, curr_type, scratches, fscratches;
+
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP)));
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL || (type & 0xff) == SLJIT_CALL_CDECL);
+
+ types = arg_types;
+ scratches = 0;
+ fscratches = 0;
+ for (i = 0; i < 5; i++) {
+ curr_type = (types & SLJIT_DEF_MASK);
+ CHECK_ARGUMENT(curr_type <= SLJIT_ARG_TYPE_F64);
+ if (i > 0) {
+ if (curr_type == 0) {
+ break;
+ }
+ if (curr_type >= SLJIT_ARG_TYPE_F32)
+ fscratches++;
+ else
+ scratches++;
+ } else {
+ if (curr_type >= SLJIT_ARG_TYPE_F32) {
+ CHECK_ARGUMENT(compiler->fscratches > 0);
+ } else if (curr_type >= SLJIT_ARG_TYPE_SW) {
+ CHECK_ARGUMENT(compiler->scratches > 0);
+ }
+ }
+ types >>= SLJIT_DEF_SHIFT;
+ }
+ CHECK_ARGUMENT(compiler->scratches >= scratches);
+ CHECK_ARGUMENT(compiler->fscratches >= fscratches);
+ CHECK_ARGUMENT(types == 0);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "]\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
@@ -1385,6 +1601,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler
CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_SIG_LESS_EQUAL);
FUNCTION_CHECK_SRC(src1, src1w);
FUNCTION_CHECK_SRC(src2, src2w);
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1404,11 +1621,12 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compile
sljit_s32 src2, sljit_sw src2w)
{
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_is_fpu_available());
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_F32_OP)));
CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL_F64 && (type & 0xff) <= SLJIT_ORDERED_F64);
FUNCTION_FCHECK(src1, src1w);
FUNCTION_FCHECK(src2, src2w);
+ compiler->last_flags = 0;
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1423,7 +1641,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compile
CHECK_RETURN_OK;
}
-static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src, sljit_sw srcw)
{
if (SLJIT_UNLIKELY(compiler->skip_checks)) {
compiler->skip_checks = 0;
@@ -1431,8 +1650,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compil
}
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_CALL3);
- CHECK_ARGUMENT(type <= SLJIT_CALL0 || (type - SLJIT_CALL0) <= compiler->scratches);
+ CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_FAST_CALL);
FUNCTION_CHECK_SRC(src, srcw);
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
@@ -1445,48 +1663,212 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compil
CHECK_RETURN_OK;
}
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 i, types, curr_type, scratches, fscratches;
+
+ CHECK_ARGUMENT(type == SLJIT_CALL || type == SLJIT_CALL_CDECL);
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ types = arg_types;
+ scratches = 0;
+ fscratches = 0;
+ for (i = 0; i < 5; i++) {
+ curr_type = (types & SLJIT_DEF_MASK);
+ CHECK_ARGUMENT(curr_type <= SLJIT_ARG_TYPE_F64);
+ if (i > 0) {
+ if (curr_type == 0) {
+ break;
+ }
+ if (curr_type >= SLJIT_ARG_TYPE_F32)
+ fscratches++;
+ else
+ scratches++;
+ } else {
+ if (curr_type >= SLJIT_ARG_TYPE_F32) {
+ CHECK_ARGUMENT(compiler->fscratches > 0);
+ } else if (curr_type >= SLJIT_ARG_TYPE_SW) {
+ CHECK_ARGUMENT(compiler->scratches > 0);
+ }
+ }
+ types >>= SLJIT_DEF_SHIFT;
+ }
+ CHECK_ARGUMENT(compiler->scratches >= scratches);
+ CHECK_ARGUMENT(compiler->fscratches >= fscratches);
+ CHECK_ARGUMENT(types == 0);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " i%s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]);
+ arg_types >>= SLJIT_DEF_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "], ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP)));
CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64);
- CHECK_ARGUMENT(op == SLJIT_MOV || GET_OPCODE(op) == SLJIT_MOV_U32 || GET_OPCODE(op) == SLJIT_MOV_S32
+ CHECK_ARGUMENT((type & 0xff) != GET_FLAG_TYPE(SLJIT_SET_CARRY) && (type & 0xff) != (GET_FLAG_TYPE(SLJIT_SET_CARRY) + 1));
+ CHECK_ARGUMENT(op == SLJIT_MOV || op == SLJIT_MOV32
|| (GET_OPCODE(op) >= SLJIT_AND && GET_OPCODE(op) <= SLJIT_XOR));
- CHECK_ARGUMENT((op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C)) == 0);
- CHECK_ARGUMENT((op & (SLJIT_SET_E | SLJIT_KEEP_FLAGS)) != (SLJIT_SET_E | SLJIT_KEEP_FLAGS));
- if (GET_OPCODE(op) < SLJIT_ADD) {
- CHECK_ARGUMENT(src == SLJIT_UNUSED && srcw == 0);
- } else {
- CHECK_ARGUMENT(src == dst && srcw == dstw);
- }
- FUNCTION_CHECK_DST(dst, dstw);
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+
+ if ((type & 0xff) <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff)
+ || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW));
+
+ FUNCTION_CHECK_DST(dst, dstw, 0);
+
+ if (GET_OPCODE(op) >= SLJIT_ADD)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z));
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
- fprintf(compiler->verbose, " flags %s%s%s%s, ",
- !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k",
+ fprintf(compiler->verbose, " flags%s %s%s, ",
+ !(op & SLJIT_SET_Z) ? "" : ".z",
GET_OPCODE(op) < SLJIT_OP2_BASE ? "mov" : op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE],
GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_I32_OP) ? "32" : ""));
sljit_verbose_param(compiler, dst, dstw);
- if (src != SLJIT_UNUSED) {
- fprintf(compiler->verbose, ", ");
- sljit_verbose_param(compiler, src, srcw);
- }
fprintf(compiler->verbose, ", %s%s\n", jump_names[type & 0xff], JUMP_POSTFIX(type));
}
#endif
CHECK_RETURN_OK;
}
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64);
+
+ CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_I32_OP));
+ if (src != SLJIT_IMM) {
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src));
+ CHECK_ARGUMENT(srcw == 0);
+ }
+
+ if ((type & 0xff) <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff)
+ || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " cmov%s %s%s, ",
+ !(dst_reg & SLJIT_I32_OP) ? "" : "32",
+ jump_names[type & 0xff], JUMP_POSTFIX(type));
+ sljit_verbose_reg(compiler, dst_reg & ~SLJIT_I32_OP);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P);
+ CHECK_ARGUMENT(!(type & SLJIT_I32_OP) || ((type & 0xff) != SLJIT_MOV && (type & 0xff) != SLJIT_MOV_U32 && (type & 0xff) != SLJIT_MOV_P));
+ CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST));
+ CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST));
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_I32_OP | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0);
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg));
+
+ CHECK_ARGUMENT((mem & REG_MASK) != SLJIT_UNUSED && (mem & REG_MASK) != reg);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (sljit_emit_mem(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED)
+ fprintf(compiler->verbose, " //");
+
+ fprintf(compiler->verbose, " mem%s.%s%s%s ",
+ !(type & SLJIT_I32_OP) ? "" : "32",
+ (type & SLJIT_MEM_STORE) ? "st" : "ld",
+ op1_names[(type & 0xff) - SLJIT_OP1_BASE],
+ (type & SLJIT_MEM_PRE) ? ".pre" : ".post");
+ sljit_verbose_reg(compiler, reg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64);
+ CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST));
+ CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST));
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_I32_OP | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0);
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (sljit_emit_fmem(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED)
+ fprintf(compiler->verbose, " //");
+
+ fprintf(compiler->verbose, " fmem.%s%s%s ",
+ (type & SLJIT_MEM_STORE) ? "st" : "ld",
+ !(type & SLJIT_I32_OP) ? ".f64" : ".f32",
+ (type & SLJIT_MEM_PRE) ? ".pre" : ".post");
+ sljit_verbose_freg(compiler, freg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
{
+ /* Any offset is allowed. */
SLJIT_UNUSED_ARG(offset);
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_DST(dst, dstw, 0);
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1503,7 +1885,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_const(struct sljit_compil
SLJIT_UNUSED_ARG(init_value);
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_DST(dst, dstw, 0);
#endif
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
if (SLJIT_UNLIKELY(!!compiler->verbose)) {
@@ -1564,6 +1946,44 @@ static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *comp
return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw);
}
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1))
+
+static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ sljit_s32 op = (dst_reg & SLJIT_I32_OP) ? SLJIT_MOV32 : SLJIT_MOV;
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+ jump = sljit_emit_jump(compiler, type ^ 0x1);
+ FAIL_IF(!jump);
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+ FAIL_IF(sljit_emit_op1(compiler, op, dst_reg & ~SLJIT_I32_OP, 0, src, srcw));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+ label = sljit_emit_label(compiler);
+ FAIL_IF(!label);
+ sljit_set_label(jump, label);
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
/* CPU description section */
#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE)
@@ -1665,6 +2085,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler
condition = SLJIT_SIG_GREATER_EQUAL;
break;
}
+
type = condition | (type & (SLJIT_I32_OP | SLJIT_REWRITABLE_JUMP));
tmp_src = src1;
src1 = src2;
@@ -1675,11 +2096,9 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler
}
if (condition <= SLJIT_NOT_ZERO)
- flags = SLJIT_SET_E;
- else if (condition <= SLJIT_LESS_EQUAL)
- flags = SLJIT_SET_U;
+ flags = SLJIT_SET_Z;
else
- flags = SLJIT_SET_S;
+ flags = condition << VARIABLE_FLAG_SHIFT;
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
@@ -1691,34 +2110,70 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP));
+ return sljit_emit_jump(compiler, condition | (type & (SLJIT_REWRITABLE_JUMP | SLJIT_I32_OP)));
}
+#endif
+
SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- sljit_s32 flags, condition;
-
CHECK_ERROR_PTR();
CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w));
- condition = type & 0xff;
- flags = (condition <= SLJIT_NOT_EQUAL_F64) ? SLJIT_SET_E : SLJIT_SET_S;
- if (type & SLJIT_F32_OP)
- flags |= SLJIT_F32_OP;
-
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- sljit_emit_fop1(compiler, SLJIT_CMP_F64 | flags, src1, src1w, src2, src2w);
+ sljit_emit_fop1(compiler, SLJIT_CMP_F64 | ((type & 0xff) << VARIABLE_FLAG_SHIFT) | (type & SLJIT_I32_OP), src1, src1w, src2, src2w);
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP));
+ return sljit_emit_jump(compiler, type);
+}
+
+#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+#endif
+
+#if !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ return SLJIT_ERR_UNSUPPORTED;
}
#endif
@@ -1736,7 +2191,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c
compiler->skip_checks = 1;
#endif
if (offset != 0)
- return sljit_emit_op2(compiler, SLJIT_ADD | SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return sljit_emit_op2(compiler, SLJIT_ADD, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
return sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_SP, 0);
}
@@ -1751,23 +2206,30 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
return "unsupported";
}
-SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void)
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data)
{
- SLJIT_ASSERT_STOP();
+ SLJIT_UNUSED_ARG(allocator_data);
+ SLJIT_UNREACHABLE();
return NULL;
}
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler)
{
SLJIT_UNUSED_ARG(compiler);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size)
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(size);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -1776,52 +2238,59 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(verbose);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
#endif
SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
{
SLJIT_UNUSED_ARG(compiler);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ SLJIT_UNUSED_ARG(feature_type);
+ SLJIT_UNREACHABLE();
+ return 0;
+}
+
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code)
{
SLJIT_UNUSED_ARG(code);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(options);
- SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(arg_types);
SLJIT_UNUSED_ARG(scratches);
SLJIT_UNUSED_ARG(saveds);
SLJIT_UNUSED_ARG(fscratches);
SLJIT_UNUSED_ARG(fsaveds);
SLJIT_UNUSED_ARG(local_size);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(options);
- SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(arg_types);
SLJIT_UNUSED_ARG(scratches);
SLJIT_UNUSED_ARG(saveds);
SLJIT_UNUSED_ARG(fscratches);
SLJIT_UNUSED_ARG(fsaveds);
SLJIT_UNUSED_ARG(local_size);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1831,7 +2300,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
SLJIT_UNUSED_ARG(op);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1840,7 +2309,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(dst);
SLJIT_UNUSED_ARG(dstw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1849,7 +2318,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1857,7 +2326,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(op);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1871,7 +2340,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
SLJIT_UNUSED_ARG(dstw);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1888,13 +2357,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
SLJIT_UNUSED_ARG(src1w);
SLJIT_UNUSED_ARG(src2);
SLJIT_UNUSED_ARG(src2w);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
{
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return reg;
}
@@ -1904,14 +2373,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(instruction);
SLJIT_UNUSED_ARG(size);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
{
- SLJIT_ASSERT_STOP();
- return 0;
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
@@ -1924,7 +2393,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
SLJIT_UNUSED_ARG(dstw);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -1941,14 +2410,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
SLJIT_UNUSED_ARG(src1w);
SLJIT_UNUSED_ARG(src2);
SLJIT_UNUSED_ARG(src2w);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
{
SLJIT_UNUSED_ARG(compiler);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -1956,7 +2425,17 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(type);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -1970,7 +2449,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler
SLJIT_UNUSED_ARG(src1w);
SLJIT_UNUSED_ARG(src2);
SLJIT_UNUSED_ARG(src2w);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -1984,7 +2463,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile
SLJIT_UNUSED_ARG(src1w);
SLJIT_UNUSED_ARG(src2);
SLJIT_UNUSED_ARG(src2w);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -1992,14 +2471,14 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sl
{
SLJIT_UNUSED_ARG(jump);
SLJIT_UNUSED_ARG(label);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target)
{
SLJIT_UNUSED_ARG(jump);
SLJIT_UNUSED_ARG(target);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
@@ -2008,23 +2487,68 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
SLJIT_UNUSED_ARG(type);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(op);
SLJIT_UNUSED_ARG(dst);
SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(dst_reg);
SLJIT_UNUSED_ARG(src);
SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
SLJIT_UNUSED_ARG(type);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -2034,7 +2558,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c
SLJIT_UNUSED_ARG(dst);
SLJIT_UNUSED_ARG(dstw);
SLJIT_UNUSED_ARG(offset);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_ERR_UNSUPPORTED;
}
@@ -2044,7 +2568,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
SLJIT_UNUSED_ARG(dst);
SLJIT_UNUSED_ARG(dstw);
SLJIT_UNUSED_ARG(initval);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
@@ -2053,7 +2577,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_ta
SLJIT_UNUSED_ARG(addr);
SLJIT_UNUSED_ARG(new_target);
SLJIT_UNUSED_ARG(executable_offset);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
@@ -2061,7 +2585,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta
SLJIT_UNUSED_ARG(addr);
SLJIT_UNUSED_ARG(new_constant);
SLJIT_UNUSED_ARG(executable_offset);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
#endif
diff --git a/thirdparty/pcre2/src/sljit/sljitLir.h b/thirdparty/pcre2/src/sljit/sljitLir.h
index f24f556b56..920f6d4f78 100644
--- a/thirdparty/pcre2/src/sljit/sljitLir.h
+++ b/thirdparty/pcre2/src/sljit/sljitLir.h
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -120,8 +120,8 @@ of sljitConfigInternal.h */
If an architecture provides two scratch and three saved registers,
its scratch and saved register sets are the following:
- R0 | [S4] | R0 and S4 represent the same physical register
- R1 | [S3] | R1 and S3 represent the same physical register
+ R0 | | R0 is always a scratch register
+ R1 | | R1 is always a scratch register
[R2] | S2 | R2 and S2 represent the same physical register
[R3] | S1 | R3 and S1 represent the same physical register
[R4] | S0 | R4 and S0 represent the same physical register
@@ -129,38 +129,35 @@ of sljitConfigInternal.h */
Note: SLJIT_NUMBER_OF_SCRATCH_REGISTERS would be 2 and
SLJIT_NUMBER_OF_SAVED_REGISTERS would be 3 for this architecture.
- Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 10
- and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 5. However, 4 registers
+ Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 12
+ and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 6. However, 6 registers
are virtual on x86-32. See below.
- The purpose of this definition is convenience. Although a register
- is either scratch register or saved register, SLJIT allows accessing
- them from the other set. For example, four registers can be used as
- scratch registers and the fifth one as saved register on the architecture
- above. Of course the last two scratch registers (R2 and R3) from this
- four will be saved on the stack, because they are defined as saved
- registers in the application binary interface. Still R2 and R3 can be
- used for referencing to these registers instead of S2 and S1, which
- makes easier to write platform independent code. Scratch registers
- can be saved registers in a similar way, but these extra saved
- registers will not be preserved across function calls! Hence the
- application must save them on those platforms, where the number of
- saved registers is too low. This can be done by copy them onto
- the stack and restore them after a function call.
+ The purpose of this definition is convenience: saved registers can
+ be used as extra scratch registers. For example four registers can
+ be specified as scratch registers and the fifth one as saved register
+ on the CPU above and any user code which requires four scratch
+ registers can run unmodified. The SLJIT compiler automatically saves
+ the content of the two extra scrath register on the stack. Scratch
+ registers can also be preserved by saving their value on the stack
+ but this needs to be done manually.
Note: To emphasize that registers assigned to R2-R4 are saved
- registers, they are enclosed by square brackets. S3-S4
- are marked in a similar way.
+ registers, they are enclosed by square brackets.
Note: sljit_emit_enter and sljit_set_context defines whether a register
is S or R register. E.g: when 3 scratches and 1 saved is mapped
by sljit_emit_enter, the allowed register set will be: R0-R2 and
S0. Although S2 is mapped to the same position as R2, it does not
- available in the current configuration. Furthermore the R3 (S1)
- register does not available as well.
+ available in the current configuration. Furthermore the S1 register
+ is not available at all.
*/
-/* When SLJIT_UNUSED is specified as destination, the result is discarded. */
+/* When SLJIT_UNUSED is specified as the destination of sljit_emit_op1
+ or sljit_emit_op2 operations the result is discarded. If no status
+ flags are set, no instructions are emitted for these operations. Data
+ prefetch is a special exception, see SLJIT_MOV operation. Other SLJIT
+ operations do not support SLJIT_UNUSED as a destination operand. */
#define SLJIT_UNUSED 0
/* Scratch registers. */
@@ -216,14 +213,6 @@ of sljitConfigInternal.h */
#define SLJIT_RETURN_REG SLJIT_R0
-/* x86 prefers specific registers for special purposes. In case of shift
- by register it supports only SLJIT_R2 for shift argument
- (which is the src2 argument of sljit_emit_op2). If another register is
- used, sljit must exchange data between registers which cause a minor
- slowdown. Other architectures has no such limitation. */
-
-#define SLJIT_PREF_SHIFT_REG SLJIT_R2
-
/* --------------------------------------------------------------------- */
/* Floating point registers */
/* --------------------------------------------------------------------- */
@@ -261,6 +250,79 @@ of sljitConfigInternal.h */
#define SLJIT_FIRST_SAVED_FLOAT_REG (SLJIT_FS0 - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS + 1)
/* --------------------------------------------------------------------- */
+/* Argument type definitions */
+/* --------------------------------------------------------------------- */
+
+/* Argument type definitions.
+ Used by SLJIT_[DEF_]ARGx and SLJIT_[DEF]_RET macros. */
+
+#define SLJIT_ARG_TYPE_VOID 0
+#define SLJIT_ARG_TYPE_SW 1
+#define SLJIT_ARG_TYPE_UW 2
+#define SLJIT_ARG_TYPE_S32 3
+#define SLJIT_ARG_TYPE_U32 4
+#define SLJIT_ARG_TYPE_F32 5
+#define SLJIT_ARG_TYPE_F64 6
+
+/* The following argument type definitions are used by sljit_emit_enter,
+ sljit_set_context, sljit_emit_call, and sljit_emit_icall functions.
+ The following return type definitions are used by sljit_emit_call
+ and sljit_emit_icall functions.
+
+ When a function is called, the first integer argument must be placed
+ in SLJIT_R0, the second in SLJIT_R1, and so on. Similarly the first
+ floating point argument must be placed in SLJIT_FR0, the second in
+ SLJIT_FR1, and so on.
+
+ Example function definition:
+ sljit_f32 SLJIT_FUNC example_c_callback(sljit_sw arg_a,
+ sljit_f64 arg_b, sljit_u32 arg_c, sljit_f32 arg_d);
+
+ Argument type definition:
+ SLJIT_DEF_RET(SLJIT_ARG_TYPE_F32)
+ | SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_SW) | SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_F64)
+ | SLJIT_DEF_ARG3(SLJIT_ARG_TYPE_U32) | SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_F32)
+
+ Short form of argument type definition:
+ SLJIT_RET(F32) | SLJIT_ARG1(SW) | SLJIT_ARG2(F64)
+ | SLJIT_ARG3(S32) | SLJIT_ARG4(F32)
+
+ Argument passing:
+ arg_a must be placed in SLJIT_R0
+ arg_c must be placed in SLJIT_R1
+ arg_b must be placed in SLJIT_FR0
+ arg_d must be placed in SLJIT_FR1
+
+Note:
+ The SLJIT_ARG_TYPE_VOID type is only supported by
+ SLJIT_DEF_RET, and SLJIT_ARG_TYPE_VOID is also the
+ default value when SLJIT_DEF_RET is not specified. */
+#define SLJIT_DEF_SHIFT 4
+#define SLJIT_DEF_RET(type) (type)
+#define SLJIT_DEF_ARG1(type) ((type) << SLJIT_DEF_SHIFT)
+#define SLJIT_DEF_ARG2(type) ((type) << (2 * SLJIT_DEF_SHIFT))
+#define SLJIT_DEF_ARG3(type) ((type) << (3 * SLJIT_DEF_SHIFT))
+#define SLJIT_DEF_ARG4(type) ((type) << (4 * SLJIT_DEF_SHIFT))
+
+/* Short form of the macros above.
+
+ For example the following definition:
+ SLJIT_DEF_RET(SLJIT_ARG_TYPE_SW) | SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_F32)
+
+ can be shortened to:
+ SLJIT_RET(SW) | SLJIT_ARG1(F32)
+
+Note:
+ The VOID type is only supported by SLJIT_RET, and
+ VOID is also the default value when SLJIT_RET is
+ not specified. */
+#define SLJIT_RET(type) SLJIT_DEF_RET(SLJIT_ARG_TYPE_ ## type)
+#define SLJIT_ARG1(type) SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_ ## type)
+#define SLJIT_ARG2(type) SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_ ## type)
+#define SLJIT_ARG3(type) SLJIT_DEF_ARG3(SLJIT_ARG_TYPE_ ## type)
+#define SLJIT_ARG4(type) SLJIT_DEF_ARG4(SLJIT_ARG_TYPE_ ## type)
+
+/* --------------------------------------------------------------------- */
/* Main structures and functions */
/* --------------------------------------------------------------------- */
@@ -332,14 +394,16 @@ struct sljit_compiler {
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
sljit_s32 args;
+ sljit_s32 locals_offset;
+ sljit_s32 saveds_offset;
+ sljit_s32 stack_tmp_size;
#endif
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
sljit_s32 mode32;
+#ifdef _WIN64
+ sljit_s32 locals_offset;
#endif
-
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
- sljit_s32 flags_saved;
#endif
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
@@ -356,24 +420,10 @@ struct sljit_compiler {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
/* Temporary fields. */
sljit_uw shift_imm;
- sljit_s32 cache_arg;
- sljit_sw cache_argw;
-#endif
-
-#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
- sljit_s32 cache_arg;
- sljit_sw cache_argw;
-#endif
-
-#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
- sljit_s32 cache_arg;
- sljit_sw cache_argw;
#endif
#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
sljit_sw imm;
- sljit_s32 cache_arg;
- sljit_sw cache_argw;
#endif
#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
@@ -399,6 +449,9 @@ struct sljit_compiler {
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|| (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ /* Flags specified by the last arithmetic instruction.
+ It contains the type of the variable flag. */
+ sljit_s32 last_flags;
/* Local size passed to the functions. */
sljit_s32 logical_local_size;
#endif
@@ -406,6 +459,7 @@ struct sljit_compiler {
#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
|| (defined SLJIT_DEBUG && SLJIT_DEBUG) \
|| (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ /* Trust arguments when the API function is called. */
sljit_s32 skip_checks;
#endif
};
@@ -491,31 +545,57 @@ static SLJIT_INLINE sljit_sw sljit_get_executable_offset(struct sljit_compiler *
*/
static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; }
+/* Returns with non-zero if the feature or limitation type passed as its
+ argument is present on the current CPU.
+
+ Some features (e.g. floating point operations) require hardware (CPU)
+ support while others (e.g. move with update) are emulated if not available.
+ However even if a feature is emulated, specialized code paths can be faster
+ than the emulation. Some limitations are emulated as well so their general
+ case is supported but it has extra performance costs. */
+
+/* [Not emulated] Floating-point support is available. */
+#define SLJIT_HAS_FPU 0
+/* [Limitation] Some registers are virtual registers. */
+#define SLJIT_HAS_VIRTUAL_REGISTERS 1
+/* [Emulated] Count leading zero is supported. */
+#define SLJIT_HAS_CLZ 2
+/* [Emulated] Conditional move is supported. */
+#define SLJIT_HAS_CMOV 3
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+/* [Not emulated] SSE2 support is available on x86. */
+#define SLJIT_HAS_SSE2 100
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type);
+
/* Instruction generation. Returns with any error code. If there is no
error, they return with SLJIT_SUCCESS. */
/*
- The executable code is a function call from the viewpoint of the C
+ The executable code is a function from the viewpoint of the C
language. The function calls must obey to the ABI (Application
Binary Interface) of the platform, which specify the purpose of
- all machine registers and stack handling among other things. The
+ machine registers and stack handling among other things. The
sljit_emit_enter function emits the necessary instructions for
setting up a new context for the executable code and moves function
arguments to the saved registers. Furthermore the options argument
can be used to pass configuration options to the compiler. The
available options are listed before sljit_emit_enter.
- The number of sljit_sw arguments passed to the generated function
- are specified in the "args" parameter. The number of arguments must
- be less than or equal to 3. The first argument goes to SLJIT_S0,
- the second goes to SLJIT_S1 and so on. The register set used by
- the function must be declared as well. The number of scratch and
- saved registers used by the function must be passed to sljit_emit_enter.
- Only R registers between R0 and "scratches" argument can be used
- later. E.g. if "scratches" is set to 2, the register set will be
- limited to R0 and R1. The S registers and the floating point
+ The function argument list is the combination of SLJIT_ARGx
+ (SLJIT_DEF_ARG1) macros. Currently maximum 3 SW / UW
+ (SLJIT_ARG_TYPE_SW / LJIT_ARG_TYPE_UW) arguments are supported.
+ The first argument goes to SLJIT_S0, the second goes to SLJIT_S1
+ and so on. The register set used by the function must be declared
+ as well. The number of scratch and saved registers used by the
+ function must be passed to sljit_emit_enter. Only R registers
+ between R0 and "scratches" argument can be used later. E.g. if
+ "scratches" is set to 2, the scratch register set will be limited
+ to SLJIT_R0 and SLJIT_R1. The S registers and the floating point
registers ("fscratches" and "fsaveds") are specified in a similar
- way. The sljit_emit_enter is also capable of allocating a stack
+ manner. The sljit_emit_enter is also capable of allocating a stack
space for local variables. The "local_size" argument contains the
size in bytes of this local area and its staring address is stored
in SLJIT_SP. The memory area between SLJIT_SP (inclusive) and
@@ -535,14 +615,14 @@ static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler
*/
/* The absolute address returned by sljit_get_local_base with
-offset 0 is aligned to sljit_d. Otherwise it is aligned to sljit_uw. */
-#define SLJIT_DOUBLE_ALIGNMENT 0x00000001
+offset 0 is aligned to sljit_f64. Otherwise it is aligned to sljit_sw. */
+#define SLJIT_F64_ALIGNMENT 0x00000001
/* The local_size must be >= 0 and <= SLJIT_MAX_LOCAL_SIZE. */
#define SLJIT_MAX_LOCAL_SIZE 65536
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
/* The machine code has a context (which contains the local stack space size,
@@ -556,7 +636,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
the previous context. */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
/* Return from machine code. The op argument can be SLJIT_UNUSED which means the
@@ -568,26 +648,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 src, sljit_sw srcw);
-/* Fast calling mechanism for utility functions (see SLJIT_FAST_CALL). All registers and
- even the stack frame is passed to the callee. The return address is preserved in
- dst/dstw by sljit_emit_fast_enter (the type of the value stored by this function
- is sljit_p), and sljit_emit_fast_return can use this as a return value later. */
+/* Generating entry and exit points for fast call functions (see SLJIT_FAST_CALL).
+ Both sljit_emit_fast_enter and sljit_emit_fast_return functions preserve the
+ values of all registers and stack frame. The return address is stored in the
+ dst argument of sljit_emit_fast_enter, and this return address can be passed
+ to sljit_emit_fast_return to continue the execution after the fast call.
-/* Note: only for sljit specific, non ABI compilant calls. Fast, since only a few machine
- instructions are needed. Excellent for small uility functions, where saving registers
- and setting up a new stack frame would cost too much performance. However, it is still
- possible to return to the address of the caller (or anywhere else). */
+ Fast calls are cheap operations (usually only a single call instruction is
+ emitted) but they do not preserve any registers. However the callee function
+ can freely use / update any registers and stack values which can be
+ efficiently exploited by various optimizations. Registers can be saved
+ manually by the callee function if needed.
-/* Note: flags are not changed (unlike sljit_emit_enter / sljit_emit_return). */
+ Although returning to different address by sljit_emit_fast_return is possible,
+ this address usually cannot be predicted by the return address predictor of
+ modern CPUs which may reduce performance. Furthermore using sljit_emit_ijump
+ to return is also inefficient since return address prediction is usually
+ triggered by a specific form of ijump.
-/* Note: although sljit_emit_fast_return could be replaced by an ijump, it is not suggested,
- since many architectures do clever branch prediction on call / return instruction pairs. */
+ Flags: - (does not modify flags). */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw);
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw);
/*
- Source and destination values for arithmetical instructions
+ Source and destination operands for arithmetical instructions
imm - a simple immediate value (cannot be used as a destination)
reg - any of the registers (immediate argument must be 0)
[imm] - absolute immediate memory address
@@ -628,6 +713,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
arm-t2: [reg+imm], -255 <= imm <= 4095
[reg+(reg<<imm)] is supported
Write back is supported only for [reg+imm], where -255 <= imm <= 255
+ arm64: [reg+imm], -256 <= imm <= 255, 0 <= aligned imm <= 4095 * alignment
+ [reg+(reg<<imm)] is supported
+ Write back is supported only for [reg+imm], where -256 <= imm <= 255
ppc: [reg+imm], -65536 <= imm <= 65535. 64 bit loads/stores and 32 bit
signed load on 64 bit requires immediates divisible by 4.
[reg+imm] is not supported for signed 8 bit values.
@@ -639,65 +727,104 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
[reg+reg] is supported
*/
-/* Register output: simply the name of the register.
- For destination, you can use SLJIT_UNUSED as well. */
+/* Macros for specifying operand types. */
#define SLJIT_MEM 0x80
#define SLJIT_MEM0() (SLJIT_MEM)
#define SLJIT_MEM1(r1) (SLJIT_MEM | (r1))
#define SLJIT_MEM2(r1, r2) (SLJIT_MEM | (r1) | ((r2) << 8))
#define SLJIT_IMM 0x40
-/* Set 32 bit operation mode (I) on 64 bit CPUs. This flag is ignored on 32
- bit CPUs. When this flag is set for an arithmetic operation, only the
- lower 32 bit of the input register(s) are used, and the CPU status flags
- are set according to the 32 bit result. Although the higher 32 bit of
- the input and the result registers are not defined by SLJIT, it might be
- defined by the CPU architecture (e.g. MIPS). To satisfy these requirements
- all source registers must be computed by operations where this flag is
- also set. In other words 32 and 64 bit arithmetic operations cannot be
- mixed. The only exception is SLJIT_IMOV and SLJIT_IMOVU whose source
- register can hold any 32 or 64 bit value. This source register is
- converted to a 32 bit compatible format. SLJIT does not generate any
- instructions on certain CPUs (e.g. on x86 and ARM) if the source and
- destination operands are the same registers. Affects sljit_emit_op0,
- sljit_emit_op1 and sljit_emit_op2. */
+/* Set 32 bit operation mode (I) on 64 bit CPUs. This option is ignored on
+ 32 bit CPUs. When this option is set for an arithmetic operation, only
+ the lower 32 bit of the input registers are used, and the CPU status
+ flags are set according to the 32 bit result. Although the higher 32 bit
+ of the input and the result registers are not defined by SLJIT, it might
+ be defined by the CPU architecture (e.g. MIPS). To satisfy these CPU
+ requirements all source registers must be the result of those operations
+ where this option was also set. Memory loads read 32 bit values rather
+ than 64 bit ones. In other words 32 bit and 64 bit operations cannot
+ be mixed. The only exception is SLJIT_MOV32 and SLJIT_MOVU32 whose source
+ register can hold any 32 or 64 bit value, and it is converted to a 32 bit
+ compatible format first. This conversion is free (no instructions are
+ emitted) on most CPUs. A 32 bit value can also be coverted to a 64 bit
+ value by SLJIT_MOV_S32 (sign extension) or SLJIT_MOV_U32 (zero extension).
+
+ Note: memory addressing always uses 64 bit values on 64 bit systems so
+ the result of a 32 bit operation must not be used with SLJIT_MEMx
+ macros.
+
+ This option is part of the instruction name, so there is no need to
+ manually set it. E.g:
+
+ SLJIT_ADD32 == (SLJIT_ADD | SLJIT_I32_OP) */
#define SLJIT_I32_OP 0x100
-/* F32 precision mode (SP). This flag is similar to SLJIT_I32_OP, just
- it applies to floating point registers (it is even the same bit). When
- this flag is passed, the CPU performs 32 bit floating point operations.
- Similar to SLJIT_I32_OP, all register arguments must be computed by
- floating point operations where this flag is also set. Affects
- sljit_emit_fop1, sljit_emit_fop2 and sljit_emit_fcmp. */
-#define SLJIT_F32_OP 0x100
-
-/* Common CPU status flags for all architectures (x86, ARM, PPC)
- - carry flag
- - overflow flag
- - zero flag
- - negative/positive flag (depends on arc)
- On mips, these flags are emulated by software. */
-
-/* By default, the instructions may, or may not set the CPU status flags.
- Forcing to set or keep status flags can be done with the following flags: */
-
-/* Note: sljit tries to emit the minimum number of instructions. Using these
- flags can increase them, so use them wisely to avoid unnecessary code generation. */
-
-/* Set Equal (Zero) status flag (E). */
-#define SLJIT_SET_E 0x0200
-/* Set unsigned status flag (U). */
-#define SLJIT_SET_U 0x0400
-/* Set signed status flag (S). */
-#define SLJIT_SET_S 0x0800
-/* Set signed overflow flag (O). */
-#define SLJIT_SET_O 0x1000
-/* Set carry flag (C).
- Note: Kinda unsigned overflow, but behaves differently on various cpus. */
-#define SLJIT_SET_C 0x2000
-/* Do not modify the flags (K).
- Note: This flag cannot be combined with any other SLJIT_SET_* flag. */
-#define SLJIT_KEEP_FLAGS 0x4000
+/* Set F32 (single) precision mode for floating-point computation. This
+ option is similar to SLJIT_I32_OP, it just applies to floating point
+ registers. When this option is passed, the CPU performs 32 bit floating
+ point operations, rather than 64 bit one. Similar to SLJIT_I32_OP, all
+ register arguments must be the result of those operations where this
+ option was also set.
+
+ This option is part of the instruction name, so there is no need to
+ manually set it. E.g:
+
+ SLJIT_MOV_F32 = (SLJIT_MOV_F64 | SLJIT_F32_OP)
+ */
+#define SLJIT_F32_OP SLJIT_I32_OP
+
+/* Many CPUs (x86, ARM, PPC) has status flags which can be set according
+ to the result of an operation. Other CPUs (MIPS) does not have status
+ flags, and results must be stored in registers. To cover both architecture
+ types efficiently only two flags are defined by SLJIT:
+
+ * Zero (equal) flag: it is set if the result is zero
+ * Variable flag: its value is defined by the last arithmetic operation
+
+ SLJIT instructions can set any or both of these flags. The value of
+ these flags is undefined if the instruction does not specify their value.
+ The description of each instruction contains the list of allowed flag
+ types.
+
+ Example: SLJIT_ADD can set the Z, OVERFLOW, CARRY flags hence
+
+ sljit_op2(..., SLJIT_ADD, ...)
+ Both the zero and variable flags are undefined so they can
+ have any value after the operation is completed.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ The variable flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_OVERFLOW, ...)
+ Sets the variable flag if an integer overflow occurs, clears
+ it otherwise. The zero flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z | SLJIT_SET_CARRY, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ Sets the variable flag if unsigned overflow (carry) occurs,
+ clears it otherwise.
+
+ If an instruction (e.g. SLJIT_MOV) does not modify flags the flags are
+ unchanged.
+
+ Using these flags can reduce the number of emitted instructions. E.g. a
+ fast loop can be implemented by decreasing a counter register and set the
+ zero flag to jump back if the counter register is not reached zero.
+
+ Motivation: although CPUs can set a large number of flags, usually their
+ values are ignored or only one of them is used. Emulating a large number
+ of flags on systems without flag register is complicated so SLJIT
+ instructions must specify the flag they want to use and only that flag
+ will be emulated. The last arithmetic instruction can be repeated if
+ multiple flags needs to be checked.
+*/
+
+/* Set Zero status flag. */
+#define SLJIT_SET_Z 0x0200
+/* Set the variable status flag if condition is true.
+ See comparison types. */
+#define SLJIT_SET(condition) ((condition) << 10)
/* Notes:
- you cannot postpone conditional jump instructions except if noted that
@@ -707,11 +834,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
/* Starting index of opcodes for sljit_emit_op0. */
#define SLJIT_OP0_BASE 0
-/* Flags: - (never set any flags)
+/* Flags: - (does not modify flags)
Note: breakpoint instruction is not supported by all architectures (e.g. ppc)
It falls back to SLJIT_NOP in those cases. */
#define SLJIT_BREAKPOINT (SLJIT_OP0_BASE + 0)
-/* Flags: - (never set any flags)
+/* Flags: - (does not modify flags)
Note: may or may not cause an extra cycle wait
it can even decrease the runtime in a few cases. */
#define SLJIT_NOP (SLJIT_OP0_BASE + 1)
@@ -723,13 +850,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
Signed multiplication of SLJIT_R0 and SLJIT_R1.
Result is placed into SLJIT_R1:SLJIT_R0 (high:low) word */
#define SLJIT_LMUL_SW (SLJIT_OP0_BASE + 3)
-/* Flags: I - (may destroy flags)
+/* Flags: - (may destroy flags)
Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
Note: if SLJIT_R1 is 0, the behaviour is undefined. */
#define SLJIT_DIVMOD_UW (SLJIT_OP0_BASE + 4)
#define SLJIT_DIVMOD_U32 (SLJIT_DIVMOD_UW | SLJIT_I32_OP)
-/* Flags: I - (may destroy flags)
+/* Flags: - (may destroy flags)
Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
Note: if SLJIT_R1 is 0, the behaviour is undefined.
@@ -737,13 +864,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
the behaviour is undefined. */
#define SLJIT_DIVMOD_SW (SLJIT_OP0_BASE + 5)
#define SLJIT_DIVMOD_S32 (SLJIT_DIVMOD_SW | SLJIT_I32_OP)
-/* Flags: I - (may destroy flags)
+/* Flags: - (may destroy flags)
Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
Note: if SLJIT_R1 is 0, the behaviour is undefined. */
#define SLJIT_DIV_UW (SLJIT_OP0_BASE + 6)
#define SLJIT_DIV_U32 (SLJIT_DIV_UW | SLJIT_I32_OP)
-/* Flags: I - (may destroy flags)
+/* Flags: - (may destroy flags)
Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
Note: if SLJIT_R1 is 0, the behaviour is undefined.
@@ -757,76 +884,67 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
/* Starting index of opcodes for sljit_emit_op1. */
#define SLJIT_OP1_BASE 32
-/* Notes for MOV instructions:
- U = Mov with update (pre form). If source or destination defined as SLJIT_MEM1(r1)
- or SLJIT_MEM2(r1, r2), r1 is increased by the sum of r2 and the constant argument
- UB = unsigned byte (8 bit)
- SB = signed byte (8 bit)
- UH = unsigned half (16 bit)
- SH = signed half (16 bit)
- UI = unsigned int (32 bit)
- SI = signed int (32 bit)
- P = pointer (sljit_p) size */
-
-/* Flags: - (never set any flags) */
+/* The MOV instruction transfer data from source to destination.
+
+ MOV instruction suffixes:
+
+ U8 - unsigned 8 bit data transfer
+ S8 - signed 8 bit data transfer
+ U16 - unsigned 16 bit data transfer
+ S16 - signed 16 bit data transfer
+ U32 - unsigned int (32 bit) data transfer
+ S32 - signed int (32 bit) data transfer
+ P - pointer (sljit_p) data transfer
+
+ If the destination of a MOV instruction is SLJIT_UNUSED and the source
+ operand is a memory address the compiler emits a prefetch instruction
+ if this instruction is supported by the current CPU. Higher data sizes
+ bring the data closer to the core: a MOV with word size loads the data
+ into a higher level cache than a byte size. Otherwise the type does not
+ affect the prefetch instruction. Furthermore a prefetch instruction
+ never fails, so it can be used to prefetch a data from an address and
+ check whether that address is NULL afterwards.
+*/
+
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV (SLJIT_OP1_BASE + 0)
-/* Flags: I - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV_U8 (SLJIT_OP1_BASE + 1)
#define SLJIT_MOV32_U8 (SLJIT_MOV_U8 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV_S8 (SLJIT_OP1_BASE + 2)
#define SLJIT_MOV32_S8 (SLJIT_MOV_S8 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV_U16 (SLJIT_OP1_BASE + 3)
#define SLJIT_MOV32_U16 (SLJIT_MOV_U16 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV_S16 (SLJIT_OP1_BASE + 4)
#define SLJIT_MOV32_S16 (SLJIT_MOV_S16 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags)
+/* Flags: - (does not modify flags)
Note: no SLJIT_MOV32_U32 form, since it is the same as SLJIT_MOV32 */
#define SLJIT_MOV_U32 (SLJIT_OP1_BASE + 5)
-/* Flags: I - (never set any flags)
+/* Flags: - (does not modify flags)
Note: no SLJIT_MOV32_S32 form, since it is the same as SLJIT_MOV32 */
#define SLJIT_MOV_S32 (SLJIT_OP1_BASE + 6)
-/* Flags: I - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV32 (SLJIT_MOV_S32 | SLJIT_I32_OP)
-/* Flags: - (never set any flags) */
+/* Flags: - (does not modify flags)
+ Note: load a pointer sized data, useful on x32 (a 32 bit mode on x86-64
+ where all x64 features are available, e.g. 16 register) or similar
+ compiling modes */
#define SLJIT_MOV_P (SLJIT_OP1_BASE + 7)
-/* Flags: - (never set any flags) */
-#define SLJIT_MOVU (SLJIT_OP1_BASE + 8)
-/* Flags: I - (never set any flags) */
-#define SLJIT_MOVU_U8 (SLJIT_OP1_BASE + 9)
-#define SLJIT_MOVU32_U8 (SLJIT_MOVU_U8 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
-#define SLJIT_MOVU_S8 (SLJIT_OP1_BASE + 10)
-#define SLJIT_MOVU32_S8 (SLJIT_MOVU_S8 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
-#define SLJIT_MOVU_U16 (SLJIT_OP1_BASE + 11)
-#define SLJIT_MOVU32_U16 (SLJIT_MOVU_U16 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags) */
-#define SLJIT_MOVU_S16 (SLJIT_OP1_BASE + 12)
-#define SLJIT_MOVU32_S16 (SLJIT_MOVU_S16 | SLJIT_I32_OP)
-/* Flags: I - (never set any flags)
- Note: no SLJIT_MOVU32_U32 form, since it is the same as SLJIT_MOVU32 */
-#define SLJIT_MOVU_U32 (SLJIT_OP1_BASE + 13)
-/* Flags: I - (never set any flags)
- Note: no SLJIT_MOVU32_S32 form, since it is the same as SLJIT_MOVU32 */
-#define SLJIT_MOVU_S32 (SLJIT_OP1_BASE + 14)
-/* Flags: I - (never set any flags) */
-#define SLJIT_MOVU32 (SLJIT_MOVU_S32 | SLJIT_I32_OP)
-/* Flags: - (never set any flags) */
-#define SLJIT_MOVU_P (SLJIT_OP1_BASE + 15)
-/* Flags: I | E | K */
-#define SLJIT_NOT (SLJIT_OP1_BASE + 16)
+/* Flags: Z
+ Note: immediate source argument is not supported */
+#define SLJIT_NOT (SLJIT_OP1_BASE + 8)
#define SLJIT_NOT32 (SLJIT_NOT | SLJIT_I32_OP)
-/* Flags: I | E | O | K */
-#define SLJIT_NEG (SLJIT_OP1_BASE + 17)
+/* Flags: Z | OVERFLOW
+ Note: immediate source argument is not supported */
+#define SLJIT_NEG (SLJIT_OP1_BASE + 9)
#define SLJIT_NEG32 (SLJIT_NEG | SLJIT_I32_OP)
/* Count leading zeroes
- Flags: I | E | K
- Important note! Sparc 32 does not support K flag, since
- the required popc instruction is introduced only in sparc 64. */
-#define SLJIT_CLZ (SLJIT_OP1_BASE + 18)
+ Flags: - (may destroy flags)
+ Note: immediate source argument is not supported */
+#define SLJIT_CLZ (SLJIT_OP1_BASE + 10)
#define SLJIT_CLZ32 (SLJIT_CLZ | SLJIT_I32_OP)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
@@ -836,46 +954,48 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
/* Starting index of opcodes for sljit_emit_op2. */
#define SLJIT_OP2_BASE 96
-/* Flags: I | E | O | C | K */
+/* Flags: Z | OVERFLOW | CARRY */
#define SLJIT_ADD (SLJIT_OP2_BASE + 0)
#define SLJIT_ADD32 (SLJIT_ADD | SLJIT_I32_OP)
-/* Flags: I | C | K */
+/* Flags: CARRY */
#define SLJIT_ADDC (SLJIT_OP2_BASE + 1)
#define SLJIT_ADDC32 (SLJIT_ADDC | SLJIT_I32_OP)
-/* Flags: I | E | U | S | O | C | K */
+/* Flags: Z | LESS | GREATER_EQUAL | GREATER | LESS_EQUAL
+ SIG_LESS | SIG_GREATER_EQUAL | SIG_GREATER
+ SIG_LESS_EQUAL | CARRY */
#define SLJIT_SUB (SLJIT_OP2_BASE + 2)
#define SLJIT_SUB32 (SLJIT_SUB | SLJIT_I32_OP)
-/* Flags: I | C | K */
+/* Flags: CARRY */
#define SLJIT_SUBC (SLJIT_OP2_BASE + 3)
#define SLJIT_SUBC32 (SLJIT_SUBC | SLJIT_I32_OP)
/* Note: integer mul
- Flags: I | O (see SLJIT_C_MUL_*) | K */
+ Flags: MUL_OVERFLOW */
#define SLJIT_MUL (SLJIT_OP2_BASE + 4)
#define SLJIT_MUL32 (SLJIT_MUL | SLJIT_I32_OP)
-/* Flags: I | E | K */
+/* Flags: Z */
#define SLJIT_AND (SLJIT_OP2_BASE + 5)
#define SLJIT_AND32 (SLJIT_AND | SLJIT_I32_OP)
-/* Flags: I | E | K */
+/* Flags: Z */
#define SLJIT_OR (SLJIT_OP2_BASE + 6)
#define SLJIT_OR32 (SLJIT_OR | SLJIT_I32_OP)
-/* Flags: I | E | K */
+/* Flags: Z */
#define SLJIT_XOR (SLJIT_OP2_BASE + 7)
#define SLJIT_XOR32 (SLJIT_XOR | SLJIT_I32_OP)
-/* Flags: I | E | K
+/* Flags: Z
Let bit_length be the length of the shift operation: 32 or 64.
If src2 is immediate, src2w is masked by (bit_length - 1).
Otherwise, if the content of src2 is outside the range from 0
to bit_length - 1, the result is undefined. */
#define SLJIT_SHL (SLJIT_OP2_BASE + 8)
#define SLJIT_SHL32 (SLJIT_SHL | SLJIT_I32_OP)
-/* Flags: I | E | K
+/* Flags: Z
Let bit_length be the length of the shift operation: 32 or 64.
If src2 is immediate, src2w is masked by (bit_length - 1).
Otherwise, if the content of src2 is outside the range from 0
to bit_length - 1, the result is undefined. */
#define SLJIT_LSHR (SLJIT_OP2_BASE + 9)
#define SLJIT_LSHR32 (SLJIT_LSHR | SLJIT_I32_OP)
-/* Flags: I | E | K
+/* Flags: Z
Let bit_length be the length of the shift operation: 32 or 64.
If src2 is immediate, src2w is masked by (bit_length - 1).
Otherwise, if the content of src2 is outside the range from 0
@@ -888,44 +1008,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w);
-/* Returns with non-zero if fpu is available. */
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void);
-
/* Starting index of opcodes for sljit_emit_fop1. */
#define SLJIT_FOP1_BASE 128
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MOV_F64 (SLJIT_FOP1_BASE + 0)
#define SLJIT_MOV_F32 (SLJIT_MOV_F64 | SLJIT_F32_OP)
/* Convert opcodes: CONV[DST_TYPE].FROM[SRC_TYPE]
SRC/DST TYPE can be: D - double, S - single, W - signed word, I - signed int
Rounding mode when the destination is W or I: round towards zero. */
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_CONV_F64_FROM_F32 (SLJIT_FOP1_BASE + 1)
#define SLJIT_CONV_F32_FROM_F64 (SLJIT_CONV_F64_FROM_F32 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_CONV_SW_FROM_F64 (SLJIT_FOP1_BASE + 2)
#define SLJIT_CONV_SW_FROM_F32 (SLJIT_CONV_SW_FROM_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_CONV_S32_FROM_F64 (SLJIT_FOP1_BASE + 3)
#define SLJIT_CONV_S32_FROM_F32 (SLJIT_CONV_S32_FROM_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_CONV_F64_FROM_SW (SLJIT_FOP1_BASE + 4)
#define SLJIT_CONV_F32_FROM_SW (SLJIT_CONV_F64_FROM_SW | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_CONV_F64_FROM_S32 (SLJIT_FOP1_BASE + 5)
#define SLJIT_CONV_F32_FROM_S32 (SLJIT_CONV_F64_FROM_S32 | SLJIT_F32_OP)
/* Note: dst is the left and src is the right operand for SLJIT_CMPD.
- Note: NaN check is always performed. If SLJIT_C_FLOAT_UNORDERED flag
- is set, the comparison result is unpredictable.
- Flags: SP | E | S (see SLJIT_C_FLOAT_*) */
+ Flags: EQUAL_F | LESS_F | GREATER_EQUAL_F | GREATER_F | LESS_EQUAL_F */
#define SLJIT_CMP_F64 (SLJIT_FOP1_BASE + 6)
#define SLJIT_CMP_F32 (SLJIT_CMP_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_NEG_F64 (SLJIT_FOP1_BASE + 7)
#define SLJIT_NEG_F32 (SLJIT_NEG_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_ABS_F64 (SLJIT_FOP1_BASE + 8)
#define SLJIT_ABS_F32 (SLJIT_ABS_F64 | SLJIT_F32_OP)
@@ -936,16 +1050,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
/* Starting index of opcodes for sljit_emit_fop2. */
#define SLJIT_FOP2_BASE 160
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_ADD_F64 (SLJIT_FOP2_BASE + 0)
#define SLJIT_ADD_F32 (SLJIT_ADD_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_SUB_F64 (SLJIT_FOP2_BASE + 1)
#define SLJIT_SUB_F32 (SLJIT_SUB_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_MUL_F64 (SLJIT_FOP2_BASE + 2)
#define SLJIT_MUL_F32 (SLJIT_MUL_F64 | SLJIT_F32_OP)
-/* Flags: SP - (never set any flags) */
+/* Flags: - (does not modify flags) */
#define SLJIT_DIV_F64 (SLJIT_FOP2_BASE + 3)
#define SLJIT_DIV_F32 (SLJIT_DIV_F64 | SLJIT_F32_OP)
@@ -972,69 +1086,98 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi
#define SLJIT_LESS 2
#define SLJIT_LESS32 (SLJIT_LESS | SLJIT_I32_OP)
+#define SLJIT_SET_LESS SLJIT_SET(SLJIT_LESS)
#define SLJIT_GREATER_EQUAL 3
#define SLJIT_GREATER_EQUAL32 (SLJIT_GREATER_EQUAL | SLJIT_I32_OP)
+#define SLJIT_SET_GREATER_EQUAL SLJIT_SET(SLJIT_GREATER_EQUAL)
#define SLJIT_GREATER 4
#define SLJIT_GREATER32 (SLJIT_GREATER | SLJIT_I32_OP)
+#define SLJIT_SET_GREATER SLJIT_SET(SLJIT_GREATER)
#define SLJIT_LESS_EQUAL 5
#define SLJIT_LESS_EQUAL32 (SLJIT_LESS_EQUAL | SLJIT_I32_OP)
+#define SLJIT_SET_LESS_EQUAL SLJIT_SET(SLJIT_LESS_EQUAL)
#define SLJIT_SIG_LESS 6
#define SLJIT_SIG_LESS32 (SLJIT_SIG_LESS | SLJIT_I32_OP)
+#define SLJIT_SET_SIG_LESS SLJIT_SET(SLJIT_SIG_LESS)
#define SLJIT_SIG_GREATER_EQUAL 7
#define SLJIT_SIG_GREATER_EQUAL32 (SLJIT_SIG_GREATER_EQUAL | SLJIT_I32_OP)
+#define SLJIT_SET_SIG_GREATER_EQUAL SLJIT_SET(SLJIT_SIG_GREATER_EQUAL)
#define SLJIT_SIG_GREATER 8
#define SLJIT_SIG_GREATER32 (SLJIT_SIG_GREATER | SLJIT_I32_OP)
+#define SLJIT_SET_SIG_GREATER SLJIT_SET(SLJIT_SIG_GREATER)
#define SLJIT_SIG_LESS_EQUAL 9
#define SLJIT_SIG_LESS_EQUAL32 (SLJIT_SIG_LESS_EQUAL | SLJIT_I32_OP)
+#define SLJIT_SET_SIG_LESS_EQUAL SLJIT_SET(SLJIT_SIG_LESS_EQUAL)
#define SLJIT_OVERFLOW 10
#define SLJIT_OVERFLOW32 (SLJIT_OVERFLOW | SLJIT_I32_OP)
+#define SLJIT_SET_OVERFLOW SLJIT_SET(SLJIT_OVERFLOW)
#define SLJIT_NOT_OVERFLOW 11
#define SLJIT_NOT_OVERFLOW32 (SLJIT_NOT_OVERFLOW | SLJIT_I32_OP)
#define SLJIT_MUL_OVERFLOW 12
#define SLJIT_MUL_OVERFLOW32 (SLJIT_MUL_OVERFLOW | SLJIT_I32_OP)
+#define SLJIT_SET_MUL_OVERFLOW SLJIT_SET(SLJIT_MUL_OVERFLOW)
#define SLJIT_MUL_NOT_OVERFLOW 13
#define SLJIT_MUL_NOT_OVERFLOW32 (SLJIT_MUL_NOT_OVERFLOW | SLJIT_I32_OP)
+/* There is no SLJIT_CARRY or SLJIT_NOT_CARRY. */
+#define SLJIT_SET_CARRY SLJIT_SET(14)
+
/* Floating point comparison types. */
-#define SLJIT_EQUAL_F64 14
+#define SLJIT_EQUAL_F64 16
#define SLJIT_EQUAL_F32 (SLJIT_EQUAL_F64 | SLJIT_F32_OP)
-#define SLJIT_NOT_EQUAL_F64 15
+#define SLJIT_SET_EQUAL_F SLJIT_SET(SLJIT_EQUAL_F64)
+#define SLJIT_NOT_EQUAL_F64 17
#define SLJIT_NOT_EQUAL_F32 (SLJIT_NOT_EQUAL_F64 | SLJIT_F32_OP)
-#define SLJIT_LESS_F64 16
+#define SLJIT_SET_NOT_EQUAL_F SLJIT_SET(SLJIT_NOT_EQUAL_F64)
+#define SLJIT_LESS_F64 18
#define SLJIT_LESS_F32 (SLJIT_LESS_F64 | SLJIT_F32_OP)
-#define SLJIT_GREATER_EQUAL_F64 17
+#define SLJIT_SET_LESS_F SLJIT_SET(SLJIT_LESS_F64)
+#define SLJIT_GREATER_EQUAL_F64 19
#define SLJIT_GREATER_EQUAL_F32 (SLJIT_GREATER_EQUAL_F64 | SLJIT_F32_OP)
-#define SLJIT_GREATER_F64 18
+#define SLJIT_SET_GREATER_EQUAL_F SLJIT_SET(SLJIT_GREATER_EQUAL_F64)
+#define SLJIT_GREATER_F64 20
#define SLJIT_GREATER_F32 (SLJIT_GREATER_F64 | SLJIT_F32_OP)
-#define SLJIT_LESS_EQUAL_F64 19
+#define SLJIT_SET_GREATER_F SLJIT_SET(SLJIT_GREATER_F64)
+#define SLJIT_LESS_EQUAL_F64 21
#define SLJIT_LESS_EQUAL_F32 (SLJIT_LESS_EQUAL_F64 | SLJIT_F32_OP)
-#define SLJIT_UNORDERED_F64 20
+#define SLJIT_SET_LESS_EQUAL_F SLJIT_SET(SLJIT_LESS_EQUAL_F64)
+#define SLJIT_UNORDERED_F64 22
#define SLJIT_UNORDERED_F32 (SLJIT_UNORDERED_F64 | SLJIT_F32_OP)
-#define SLJIT_ORDERED_F64 21
+#define SLJIT_SET_UNORDERED_F SLJIT_SET(SLJIT_UNORDERED_F64)
+#define SLJIT_ORDERED_F64 23
#define SLJIT_ORDERED_F32 (SLJIT_ORDERED_F64 | SLJIT_F32_OP)
+#define SLJIT_SET_ORDERED_F SLJIT_SET(SLJIT_ORDERED_F64)
/* Unconditional jump types. */
-#define SLJIT_JUMP 22
-#define SLJIT_FAST_CALL 23
-#define SLJIT_CALL0 24
-#define SLJIT_CALL1 25
-#define SLJIT_CALL2 26
-#define SLJIT_CALL3 27
-
-/* Fast calling method. See sljit_emit_fast_enter / sljit_emit_fast_return. */
+#define SLJIT_JUMP 24
+ /* Fast calling method. See sljit_emit_fast_enter / sljit_emit_fast_return. */
+#define SLJIT_FAST_CALL 25
+ /* Called function must be declared with the SLJIT_FUNC attribute. */
+#define SLJIT_CALL 26
+ /* Called function must be decalred with cdecl attribute.
+ This is the default attribute for C functions. */
+#define SLJIT_CALL_CDECL 27
/* The target can be changed during runtime (see: sljit_set_jump_addr). */
#define SLJIT_REWRITABLE_JUMP 0x1000
/* Emit a jump instruction. The destination is not set, only the type of the jump.
- type must be between SLJIT_EQUAL and SLJIT_CALL3
+ type must be between SLJIT_EQUAL and SLJIT_FAST_CALL
type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
- Flags: - (never set any flags) for both conditional and unconditional jumps.
- Flags: destroy all flags for calls. */
+
+ Flags: does not modify flags. */
SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type);
+/* Emit a C compiler (ABI) compatible function call.
+ type must be SLJIT_CALL or SLJIT_CALL_CDECL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+ arg_types is the combination of SLJIT_RET / SLJIT_ARGx (SLJIT_DEF_RET / SLJIT_DEF_ARGx) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types);
+
/* Basic arithmetic comparison. In most architectures it is implemented as
an SLJIT_SUB operation (with SLJIT_UNUSED destination and setting
appropriate flags) followed by a sljit_emit_jump. However some
@@ -1042,7 +1185,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
It is suggested to use this comparison form when appropriate.
type must be between SLJIT_EQUAL and SLJIT_I_SIG_LESS_EQUAL
type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
- Flags: destroy flags. */
+
+ Flags: may destroy flags. */
SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w);
@@ -1066,40 +1210,112 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sl
/* Set the destination address of the jump to this label. */
SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target);
-/* Call function or jump anywhere. Both direct and indirect form
- type must be between SLJIT_JUMP and SLJIT_CALL3
- Direct form: set src to SLJIT_IMM() and srcw to the address
- Indirect form: any other valid addressing mode
- Flags: - (never set any flags) for unconditional jumps.
- Flags: destroy all flags for calls. */
+/* Emit an indirect jump or fast call. Both direct and indirect form
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be between SLJIT_JUMP and SLJIT_FAST_CALL
+
+ Flags: does not modify flags. */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw);
+/* Emit a C compiler (ABI) compatible function call.
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be SLJIT_CALL or SLJIT_CALL_CDECL
+ arg_types is the combination of SLJIT_RET / SLJIT_ARGx (SLJIT_DEF_RET / SLJIT_DEF_ARGx) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types, sljit_s32 src, sljit_sw srcw);
+
/* Perform the operation using the conditional flags as the second argument.
- Type must always be between SLJIT_EQUAL and SLJIT_S_ORDERED. The value
+ Type must always be between SLJIT_EQUAL and SLJIT_ORDERED_F64. The value
represented by the type is 1, if the condition represented by the type
is fulfilled, and 0 otherwise.
- If op == SLJIT_MOV, SLJIT_MOV_S32, SLJIT_MOV_U32:
+ If op == SLJIT_MOV, SLJIT_MOV32:
Set dst to the value represented by the type (0 or 1).
- Src must be SLJIT_UNUSED, and srcw must be 0
- Flags: - (never set any flags)
+ Flags: - (does not modify flags)
If op == SLJIT_OR, op == SLJIT_AND, op == SLJIT_XOR
- Performs the binary operation using src as the first, and the value
- represented by type as the second argument.
- Important note: only dst=src and dstw=srcw is supported at the moment!
- Flags: I | E | K
- Note: sljit_emit_op_flags does nothing, if dst is SLJIT_UNUSED (regardless of op). */
+ Performs the binary operation using dst as the first, and the value
+ represented by type as the second argument. Result is written into dst.
+ Flags: Z (may destroy flags) */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type);
-/* Copies the base address of SLJIT_SP + offset to dst.
- Flags: - (never set any flags) */
+/* Emit a conditional mov instruction which moves source to destination,
+ if the condition is satisfied. Unlike other arithmetic operations this
+ instruction does not support memory access.
+
+ type must be between SLJIT_EQUAL and SLJIT_ORDERED_F64
+ dst_reg must be a valid register and it can be combined
+ with SLJIT_I32_OP to perform a 32 bit arithmetic operation
+ src must be register or immediate (SLJIT_IMM)
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw);
+
+/* The following flags are used by sljit_emit_mem() and sljit_emit_fmem(). */
+
+/* When SLJIT_MEM_SUPP is passed, no instructions are emitted.
+ Instead the function returns with SLJIT_SUCCESS if the instruction
+ form is supported and SLJIT_ERR_UNSUPPORTED otherwise. This flag
+ allows runtime checking of available instruction forms. */
+#define SLJIT_MEM_SUPP 0x0200
+/* Memory load operation. This is the default. */
+#define SLJIT_MEM_LOAD 0x0000
+/* Memory store operation. */
+#define SLJIT_MEM_STORE 0x0400
+/* Base register is updated before the memory access. */
+#define SLJIT_MEM_PRE 0x0800
+/* Base register is updated after the memory access. */
+#define SLJIT_MEM_POST 0x1000
+
+/* Emit a single memory load or store with update instruction. When the
+ requested instruction from is not supported by the CPU, it returns
+ with SLJIT_ERR_UNSUPPORTED instead of emulating the instruction. This
+ allows specializing tight loops based on the supported instruction
+ forms (see SLJIT_MEM_SUPP flag).
+
+ type must be between SLJIT_MOV and SLJIT_MOV_P and can be
+ combined with SLJIT_MEM_* flags. Either SLJIT_MEM_PRE
+ or SLJIT_MEM_POST must be specified.
+ reg is the source or destination register, and must be
+ different from the base register of the mem operand
+ mem must be a SLJIT_MEM1() or SLJIT_MEM2() operand
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Same as sljit_emit_mem except the followings:
+
+ type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be
+ combined with SLJIT_MEM_* flags. Either SLJIT_MEM_PRE
+ or SLJIT_MEM_POST must be specified.
+ freg is the source or destination floating point register */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Copies the base address of SLJIT_SP + offset to dst. The offset can be
+ anything to negate the effect of relative addressing. For example if an
+ array of sljit_sw values is stored on the stack from offset 0x40, and R0
+ contains the offset of an array item plus 0x120, this item can be
+ overwritten by two SLJIT instructions:
+
+ sljit_get_local_base(compiler, SLJIT_R1, 0, 0x40 - 0x120);
+ sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_MEM2(SLJIT_R1, SLJIT_R0), 0, SLJIT_IMM, 0x5);
+
+ Flags: - (may destroy flags) */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset);
/* The constant can be changed runtime (see: sljit_set_const)
- Flags: - (never set any flags) */
+ Flags: - (does not modify flags) */
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value);
/* After the code generation the address for label, jump and const instructions
@@ -1119,7 +1335,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta
/* --------------------------------------------------------------------- */
#define SLJIT_MAJOR_VERSION 0
-#define SLJIT_MINOR_VERSION 93
+#define SLJIT_MINOR_VERSION 94
/* Get the human readable name of the platform. Can be useful on platforms
like ARM, where ARM and Thumb2 functions can be mixed, and
@@ -1131,53 +1347,58 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void);
#if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK)
/* This global lock is useful to compile common functions. */
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void);
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void);
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void);
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void);
#endif
#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
-/* The sljit_stack is a utiliy feature of sljit, which allocates a
- writable memory region between base (inclusive) and limit (exclusive).
- Both base and limit is a pointer, and base is always <= than limit.
- This feature uses the "address space reserve" feature
- of modern operating systems. Basically we don't need to allocate a
- huge memory block in one step for the worst case, we can start with
- a smaller chunk and extend it later. Since the address space is
- reserved, the data never copied to other regions, thus it is safe
- to store pointers here. */
-
-/* Note: The base field is aligned to PAGE_SIZE bytes (usually 4k or more).
- Note: stack growing should not happen in small steps: 4k, 16k or even
- bigger growth is better.
- Note: this structure may not be supported by all operating systems.
- Some kind of fallback mechanism is suggested when SLJIT_UTIL_STACK
- is not defined. */
+/* The sljit_stack structure and its manipulation functions provides
+ an implementation for a top-down stack. The stack top is stored
+ in the end field of the sljit_stack structure and the stack goes
+ down to the min_start field, so the memory region reserved for
+ this stack is between min_start (inclusive) and end (exclusive)
+ fields. However the application can only use the region between
+ start (inclusive) and end (exclusive) fields. The sljit_stack_resize
+ function can be used to extend this region up to min_start.
+
+ This feature uses the "address space reserve" feature of modern
+ operating systems. Instead of allocating a large memory block
+ applications can allocate a small memory region and extend it
+ later without moving the content of the memory area. Therefore
+ after a successful resize by sljit_stack_resize all pointers into
+ this region are still valid.
+
+ Note:
+ this structure may not be supported by all operating systems.
+ end and max_limit fields are aligned to PAGE_SIZE bytes (usually
+ 4 Kbyte or more).
+ stack should grow in larger steps, e.g. 4Kbyte, 16Kbyte or more. */
struct sljit_stack {
/* User data, anything can be stored here.
- Starting with the same value as base. */
- sljit_uw top;
- /* These members are read only. */
- sljit_uw base;
- sljit_uw limit;
- sljit_uw max_limit;
+ Initialized to the same value as the end field. */
+ sljit_u8 *top;
+/* These members are read only. */
+ /* End address of the stack */
+ sljit_u8 *end;
+ /* Current start address of the stack. */
+ sljit_u8 *start;
+ /* Lowest start address of the stack. */
+ sljit_u8 *min_start;
};
-/* Returns NULL if unsuccessful.
- Note: limit and max_limit contains the size for stack allocation.
- Note: the top field is initialized to base.
+/* Allocates a new stack. Returns NULL if unsuccessful.
Note: see sljit_create_compiler for the explanation of allocator_data. */
-SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit, void *allocator_data);
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack *stack, void *allocator_data);
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data);
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data);
-/* Can be used to increase (allocate) or decrease (free) the memory area.
- Returns with a non-zero value if unsuccessful. If new_limit is greater than
- max_limit, it will fail. It is very easy to implement a stack data structure,
- since the growth ratio can be added to the current limit, and sljit_stack_resize
- will do all the necessary checks. The fields of the stack are not changed if
- sljit_stack_resize fails. */
-SLJIT_API_FUNC_ATTRIBUTE sljit_sw SLJIT_CALL sljit_stack_resize(struct sljit_stack *stack, sljit_uw new_limit);
+/* Can be used to increase (extend) or decrease (shrink) the stack
+ memory area. Returns with new_start if successful and NULL otherwise.
+ It always fails if new_start is less than min_start or greater or equal
+ than end fields. The fields of the stack are not changed if the returned
+ value is NULL (the current memory content is never lost). */
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start);
#endif /* (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) */
@@ -1206,6 +1427,15 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct
#endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+/* Free unused executable memory. The allocator keeps some free memory
+ around to reduce the number of OS executable memory allocations.
+ This improves performance since these calls are costly. However
+ it is sometimes desired to free all unused memory regions, e.g.
+ before the application terminates. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#endif
+
/* --------------------------------------------------------------------- */
/* CPU specific functions */
/* --------------------------------------------------------------------- */
@@ -1238,32 +1468,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
void *instruction, sljit_s32 size);
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
-
-/* Returns with non-zero if sse2 is available. */
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_is_sse2_available(void);
-
-/* Returns with non-zero if cmov instruction is available. */
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_is_cmov_available(void);
-
-/* Emit a conditional mov instruction on x86 CPUs. This instruction
- moves src to destination, if the condition is satisfied. Unlike
- other arithmetic instructions, destination must be a register.
- Before such instructions are emitted, cmov support should be
- checked by sljit_x86_is_cmov_available function.
- type must be between SLJIT_EQUAL and SLJIT_S_ORDERED
- dst_reg must be a valid register and it can be combined
- with SLJIT_I32_OP to perform 32 bit arithmetic
- Flags: I - (never set any flags)
- */
+/* Define the currently available CPU status flags. It is usually used after an
+ sljit_emit_op_custom call to define which flags are set. */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_emit_cmov(struct sljit_compiler *compiler,
- sljit_s32 type,
- sljit_s32 dst_reg,
- sljit_s32 src, sljit_sw srcw);
-
-#endif
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler,
+ sljit_s32 current_flags);
#endif /* _SLJIT_LIR_H_ */
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c
index 09701d53fc..6d61eed9a7 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -24,12 +24,18 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef __SOFTFP__
+#define ARM_ABI_INFO " ABI:softfp"
+#else
+#define ARM_ABI_INFO " ABI:hardfp"
+#endif
+
SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
{
#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
- return "ARMv7" SLJIT_CPUINFO;
+ return "ARMv7" SLJIT_CPUINFO ARM_ABI_INFO;
#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
- return "ARMv5" SLJIT_CPUINFO;
+ return "ARMv5" SLJIT_CPUINFO ARM_ABI_INFO;
#else
#error "Internal error: Unknown ARM architecture"
#endif
@@ -38,11 +44,10 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
/* Last register + 1. */
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
-#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 5)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_FREG1 (0)
-#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
/* In ARM instruction words.
Cache lines are usually 32 byte aligned. */
@@ -55,8 +60,12 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
(((max_diff) / (sljit_s32)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1))
/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
-static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
- 0, 0, 1, 2, 11, 10, 9, 8, 7, 6, 5, 4, 13, 3, 12, 14, 15
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7
};
#define RM(rm) (reg_map[rm])
@@ -73,31 +82,31 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define CONDITIONAL 0xe0000000
#define PUSH_POOL 0xff000000
-/* DP - Data Processing instruction (use with EMIT_DATA_PROCESS_INS). */
-#define ADC_DP 0x5
-#define ADD_DP 0x4
-#define AND_DP 0x0
+#define ADC 0xe0a00000
+#define ADD 0xe0800000
+#define AND 0xe0000000
#define B 0xea000000
-#define BIC_DP 0xe
+#define BIC 0xe1c00000
#define BL 0xeb000000
#define BLX 0xe12fff30
#define BX 0xe12fff10
#define CLZ 0xe16f0f10
-#define CMP_DP 0xa
+#define CMN 0xe1600000
+#define CMP 0xe1400000
#define BKPT 0xe1200070
-#define EOR_DP 0x1
-#define MOV_DP 0xd
+#define EOR 0xe0200000
+#define MOV 0xe1a00000
#define MUL 0xe0000090
-#define MVN_DP 0xf
+#define MVN 0xe1e00000
#define NOP 0xe1a00000
-#define ORR_DP 0xc
+#define ORR 0xe1800000
#define PUSH 0xe92d0000
#define POP 0xe8bd0000
-#define RSB_DP 0x3
-#define RSC_DP 0x7
-#define SBC_DP 0x6
+#define RSB 0xe0600000
+#define RSC 0xe0e00000
+#define SBC 0xe0c00000
#define SMULL 0xe0c00090
-#define SUB_DP 0x2
+#define SUB 0xe0400000
#define UMULL 0xe0800090
#define VABS_F32 0xeeb00ac0
#define VADD_F32 0xee300a00
@@ -108,6 +117,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define VDIV_F32 0xee800a00
#define VMOV_F32 0xeeb00a40
#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
#define VMRS 0xeef1fa10
#define VMUL_F32 0xee200a00
#define VNEG_F32 0xeeb10a40
@@ -260,6 +270,8 @@ static SLJIT_INLINE sljit_s32 emit_blx(struct sljit_compiler *compiler)
{
/* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */
SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092));
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
return push_inst(compiler, BLX | RM(TMP_REG1));
}
@@ -814,28 +826,77 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return code;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Entry, exit */
/* --------------------------------------------------------------------- */
-/* emit_op inp_flags.
- WRITE_BACK must be the first, since it is a flag. */
-#define WRITE_BACK 0x01
-#define ALLOW_IMM 0x02
-#define ALLOW_INV_IMM 0x04
+/* Creates an index in data_transfer_insts array. */
+#define WORD_SIZE 0x00
+#define BYTE_SIZE 0x01
+#define HALF_SIZE 0x02
+#define PRELOAD 0x03
+#define SIGNED 0x04
+#define LOAD_DATA 0x08
+
+/* Flag bits for emit_op. */
+#define ALLOW_IMM 0x10
+#define ALLOW_INV_IMM 0x20
#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM)
-#define ARG_TEST 0x08
-/* Creates an index in data_transfer_insts array. */
-#define WORD_DATA 0x00
-#define BYTE_DATA 0x10
-#define HALF_DATA 0x20
-#define SIGNED_DATA 0x40
-#define LOAD_DATA 0x80
+/* s/l - store/load (1 bit)
+ u/s - signed/unsigned (1 bit)
+ w/b/h/N - word/byte/half/NOT allowed (2 bit)
+ Storing signed and unsigned values are the same operations. */
+
+static const sljit_uw data_transfer_insts[16] = {
+/* s u w */ 0xe5000000 /* str */,
+/* s u b */ 0xe5400000 /* strb */,
+/* s u h */ 0xe10000b0 /* strh */,
+/* s u N */ 0x00000000 /* not allowed */,
+/* s s w */ 0xe5000000 /* str */,
+/* s s b */ 0xe5400000 /* strb */,
+/* s s h */ 0xe10000b0 /* strh */,
+/* s s N */ 0x00000000 /* not allowed */,
+
+/* l u w */ 0xe5100000 /* ldr */,
+/* l u b */ 0xe5500000 /* ldrb */,
+/* l u h */ 0xe11000b0 /* ldrh */,
+/* l u p */ 0xf5500000 /* preload */,
+/* l s w */ 0xe5100000 /* ldr */,
+/* l s b */ 0xe11000d0 /* ldrsb */,
+/* l s h */ 0xe11000f0 /* ldrsh */,
+/* l s N */ 0x00000000 /* not allowed */,
+};
-/* Condition: AL. */
-#define EMIT_DATA_PROCESS_INS(opcode, set_flags, dst, src1, src2) \
- (0xe0000000 | ((opcode) << 21) | (set_flags) | RD(dst) | RN(src1) | (src2))
+#define EMIT_DATA_TRANSFER(type, add, target_reg, base_reg, arg) \
+ (data_transfer_insts[(type) & 0xf] | ((add) << 23) | RD(target_reg) | RN(base_reg) | (arg))
+
+/* Normal ldr/str instruction.
+ Type2: ldrsb, ldrh, ldrsh */
+#define IS_TYPE1_TRANSFER(type) \
+ (data_transfer_insts[(type) & 0xf] & 0x04000000)
+#define TYPE2_TRANSFER_IMM(imm) \
+ (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22))
static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
sljit_s32 dst, sljit_sw dstw,
@@ -843,15 +904,15 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
sljit_s32 src2, sljit_sw src2w);
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 size, i, tmp;
+ sljit_s32 args, size, i, tmp;
sljit_uw push;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
/* Push saved registers, temporary registers
stmdb sp!, {..., lr} */
@@ -873,25 +934,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
if (local_size > 0)
FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size));
+ args = get_arg_count(arg_types);
+
if (args >= 1)
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S0, SLJIT_UNUSED, RM(SLJIT_R0))));
+ FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S0) | RM(SLJIT_R0)));
if (args >= 2)
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S1, SLJIT_UNUSED, RM(SLJIT_R1))));
+ FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S1) | RM(SLJIT_R1)));
if (args >= 3)
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S2, SLJIT_UNUSED, RM(SLJIT_R2))));
+ FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S2) | RM(SLJIT_R2)));
return SLJIT_SUCCESS;
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
sljit_s32 size;
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
compiler->local_size = ((size + local_size + 7) & ~7) - size;
@@ -929,52 +992,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
/* Operators */
/* --------------------------------------------------------------------- */
-/* s/l - store/load (1 bit)
- u/s - signed/unsigned (1 bit)
- w/b/h/N - word/byte/half/NOT allowed (2 bit)
- It contans 16 items, but not all are different. */
-
-static sljit_sw data_transfer_insts[16] = {
-/* s u w */ 0xe5000000 /* str */,
-/* s u b */ 0xe5400000 /* strb */,
-/* s u h */ 0xe10000b0 /* strh */,
-/* s u N */ 0x00000000 /* not allowed */,
-/* s s w */ 0xe5000000 /* str */,
-/* s s b */ 0xe5400000 /* strb */,
-/* s s h */ 0xe10000b0 /* strh */,
-/* s s N */ 0x00000000 /* not allowed */,
-
-/* l u w */ 0xe5100000 /* ldr */,
-/* l u b */ 0xe5500000 /* ldrb */,
-/* l u h */ 0xe11000b0 /* ldrh */,
-/* l u N */ 0x00000000 /* not allowed */,
-/* l s w */ 0xe5100000 /* ldr */,
-/* l s b */ 0xe11000d0 /* ldrsb */,
-/* l s h */ 0xe11000f0 /* ldrsh */,
-/* l s N */ 0x00000000 /* not allowed */,
-};
-
-#define EMIT_DATA_TRANSFER(type, add, wb, target, base1, base2) \
- (data_transfer_insts[(type) >> 4] | ((add) << 23) | ((wb) << 21) | (reg_map[target] << 12) | (reg_map[base1] << 16) | (base2))
-/* Normal ldr/str instruction.
- Type2: ldrsb, ldrh, ldrsh */
-#define IS_TYPE1_TRANSFER(type) \
- (data_transfer_insts[(type) >> 4] & 0x04000000)
-#define TYPE2_TRANSFER_IMM(imm) \
- (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22))
-
/* flags: */
/* Arguments are swapped. */
#define ARGS_SWAPPED 0x01
/* Inverted immediate. */
#define INV_IMM 0x02
/* Source and destination is register. */
-#define REG_DEST 0x04
-#define REG_SOURCE 0x08
- /* One instruction is enough. */
-#define FAST_DEST 0x10
- /* Multiple instructions are required. */
-#define SLOW_DEST 0x20
+#define MOVE_REG_CONV 0x04
+ /* Unused return value. */
+#define UNUSED_RETURN 0x08
/* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */
#define SET_FLAGS (1 << 20)
/* dst: reg
@@ -983,157 +1009,127 @@ static sljit_sw data_transfer_insts[16] = {
SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */
#define SRC2_IMM (1 << 25)
-#define EMIT_DATA_PROCESS_INS_AND_RETURN(opcode) \
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, (src2 & SRC2_IMM) ? src2 : RM(src2)))
-
-#define EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(opcode, dst, src1, src2) \
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, src2))
-
#define EMIT_SHIFT_INS_AND_RETURN(opcode) \
SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); \
if (compiler->shift_imm != 0x20) { \
SLJIT_ASSERT(src1 == TMP_REG1); \
SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); \
+ \
if (compiler->shift_imm != 0) \
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (compiler->shift_imm << 7) | (opcode << 5) | reg_map[src2])); \
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, reg_map[src2])); \
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | \
+ RD(dst) | (compiler->shift_imm << 7) | (opcode << 5) | RM(src2)); \
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2)); \
} \
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | ((flags & ARGS_SWAPPED) ? reg_map[src2] : reg_map[src1])));
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | \
+ (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | RM((flags & ARGS_SWAPPED) ? src2 : src1));
static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
{
- sljit_sw mul_inst;
-
switch (GET_OPCODE(op)) {
case SLJIT_MOV:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
if (dst != src2) {
if (src2 & SRC2_IMM) {
- if (flags & INV_IMM)
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
}
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, reg_map[src2]);
+ return push_inst(compiler, MOV | RD(dst) | RM(src2));
}
return SLJIT_SUCCESS;
case SLJIT_MOV_U8:
case SLJIT_MOV_S8:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
- if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) {
+ if (flags & MOVE_REG_CONV) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
if (op == SLJIT_MOV_U8)
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(AND_DP, 0, dst, src2, SRC2_IMM | 0xff));
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | reg_map[src2])));
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | reg_map[dst]));
+ return push_inst(compiler, AND | RD(dst) | RN(src2) | SRC2_IMM | 0xff);
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (24 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | RM(dst));
#else
return push_inst(compiler, (op == SLJIT_MOV_U8 ? UXTB : SXTB) | RD(dst) | RM(src2));
#endif
}
else if (dst != src2) {
SLJIT_ASSERT(src2 & SRC2_IMM);
- if (flags & INV_IMM)
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
}
return SLJIT_SUCCESS;
case SLJIT_MOV_U16:
case SLJIT_MOV_S16:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
- if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) {
+ if (flags & MOVE_REG_CONV) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | reg_map[src2])));
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | reg_map[dst]));
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (16 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | RM(dst));
#else
return push_inst(compiler, (op == SLJIT_MOV_U16 ? UXTH : SXTH) | RD(dst) | RM(src2));
#endif
}
else if (dst != src2) {
SLJIT_ASSERT(src2 & SRC2_IMM);
- if (flags & INV_IMM)
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
}
return SLJIT_SUCCESS;
case SLJIT_NOT:
if (src2 & SRC2_IMM) {
- if (flags & INV_IMM)
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
+ return push_inst(compiler, ((flags & INV_IMM) ? MOV : MVN) | (flags & SET_FLAGS) | RD(dst) | src2);
}
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, RM(src2));
+ return push_inst(compiler, MVN | (flags & SET_FLAGS) | RD(dst) | RM(src2));
case SLJIT_CLZ:
SLJIT_ASSERT(!(flags & INV_IMM));
SLJIT_ASSERT(!(src2 & SRC2_IMM));
FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2)));
- if (flags & SET_FLAGS)
- EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(CMP_DP, SLJIT_UNUSED, dst, SRC2_IMM);
return SLJIT_SUCCESS;
case SLJIT_ADD:
SLJIT_ASSERT(!(flags & INV_IMM));
- EMIT_DATA_PROCESS_INS_AND_RETURN(ADD_DP);
+ if ((flags & (UNUSED_RETURN | SET_FLAGS)) == (UNUSED_RETURN | SET_FLAGS) && !(flags & ARGS_SWAPPED))
+ return push_inst(compiler, CMN | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, ADD | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_ADDC:
SLJIT_ASSERT(!(flags & INV_IMM));
- EMIT_DATA_PROCESS_INS_AND_RETURN(ADC_DP);
+ return push_inst(compiler, ADC | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_SUB:
SLJIT_ASSERT(!(flags & INV_IMM));
- if (!(flags & ARGS_SWAPPED))
- EMIT_DATA_PROCESS_INS_AND_RETURN(SUB_DP);
- EMIT_DATA_PROCESS_INS_AND_RETURN(RSB_DP);
+ if ((flags & (UNUSED_RETURN | SET_FLAGS)) == (UNUSED_RETURN | SET_FLAGS) && !(flags & ARGS_SWAPPED))
+ return push_inst(compiler, CMP | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SUB : RSB) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_SUBC:
SLJIT_ASSERT(!(flags & INV_IMM));
- if (!(flags & ARGS_SWAPPED))
- EMIT_DATA_PROCESS_INS_AND_RETURN(SBC_DP);
- EMIT_DATA_PROCESS_INS_AND_RETURN(RSC_DP);
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SBC : RSC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_MUL:
SLJIT_ASSERT(!(flags & INV_IMM));
SLJIT_ASSERT(!(src2 & SRC2_IMM));
- if (SLJIT_UNLIKELY(op & SLJIT_SET_O))
- mul_inst = SMULL | (reg_map[TMP_REG3] << 16) | (reg_map[dst] << 12);
- else
- mul_inst = MUL | (reg_map[dst] << 16);
- if (dst != src2)
- FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src1] << 8) | reg_map[src2]));
- else if (dst != src1)
- FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[src1]));
- else {
- /* Rm and Rd must not be the same register. */
- SLJIT_ASSERT(dst != TMP_REG1);
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, reg_map[src2])));
- FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[TMP_REG1]));
- }
+ if (!HAS_FLAGS(op))
+ return push_inst(compiler, MUL | (reg_map[dst] << 16) | (reg_map[src2] << 8) | reg_map[src1]);
- if (!(op & SLJIT_SET_O))
- return SLJIT_SUCCESS;
+ FAIL_IF(push_inst(compiler, SMULL | (reg_map[TMP_REG1] << 16) | (reg_map[dst] << 12) | (reg_map[src2] << 8) | reg_map[src1]));
- /* We need to use TMP_REG3. */
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- /* cmp TMP_REG2, dst asr #31. */
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(CMP_DP, SET_FLAGS, SLJIT_UNUSED, TMP_REG3, RM(dst) | 0xfc0));
+ /* cmp TMP_REG1, dst asr #31. */
+ return push_inst(compiler, CMP | SET_FLAGS | RN(TMP_REG1) | RM(dst) | 0xfc0);
case SLJIT_AND:
- if (!(flags & INV_IMM))
- EMIT_DATA_PROCESS_INS_AND_RETURN(AND_DP);
- EMIT_DATA_PROCESS_INS_AND_RETURN(BIC_DP);
+ return push_inst(compiler, (!(flags & INV_IMM) ? AND : BIC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_OR:
SLJIT_ASSERT(!(flags & INV_IMM));
- EMIT_DATA_PROCESS_INS_AND_RETURN(ORR_DP);
+ return push_inst(compiler, ORR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_XOR:
SLJIT_ASSERT(!(flags & INV_IMM));
- EMIT_DATA_PROCESS_INS_AND_RETURN(EOR_DP);
+ return push_inst(compiler, EOR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
case SLJIT_SHL:
EMIT_SHIFT_INS_AND_RETURN(0);
@@ -1144,12 +1140,11 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
case SLJIT_ASHR:
EMIT_SHIFT_INS_AND_RETURN(2);
}
- SLJIT_ASSERT_STOP();
+
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
-#undef EMIT_DATA_PROCESS_INS_AND_RETURN
-#undef EMIT_FULL_DATA_PROCESS_INS_AND_RETURN
#undef EMIT_SHIFT_INS_AND_RETURN
/* Tests whether the immediate can be stored in the 12 bit imm field.
@@ -1297,8 +1292,8 @@ static sljit_s32 generate_int(struct sljit_compiler *compiler, sljit_s32 reg, sl
return 0;
}
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? MOV_DP : MVN_DP, 0, reg, SLJIT_UNUSED, imm1)));
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? ORR_DP : BIC_DP, 0, reg, reg, imm2)));
+ FAIL_IF(push_inst(compiler, (positive ? MOV : MVN) | RD(reg) | imm1));
+ FAIL_IF(push_inst(compiler, (positive ? ORR : BIC) | RD(reg) | RN(reg) | imm2));
return 1;
}
#endif
@@ -1315,11 +1310,11 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg,
/* Create imm by 1 inst. */
tmp = get_imm(imm);
if (tmp)
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, tmp));
+ return push_inst(compiler, MOV | RD(reg) | tmp);
tmp = get_imm(~imm);
if (tmp)
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, tmp));
+ return push_inst(compiler, MVN | RD(reg) | tmp);
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
/* Create imm by 2 inst. */
@@ -1327,293 +1322,109 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg,
FAIL_IF(generate_int(compiler, reg, ~imm, 0));
/* Load integer. */
- return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), imm);
+ return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), imm);
#else
- return emit_imm(compiler, reg, imm);
+ FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff)));
+ if (imm <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff));
#endif
}
-/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
-static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value)
+static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
{
- if (value >= 0) {
- value = get_imm(value);
- if (value)
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, dst, reg, value));
- }
- else {
- value = get_imm(-value);
- if (value)
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, dst, reg, value));
- }
- return SLJIT_ERR_UNSUPPORTED;
-}
+ sljit_uw imm, offset_reg;
+ sljit_uw is_type1_transfer = IS_TYPE1_TRANSFER(flags);
-/* Can perform an operation using at most 1 instruction. */
-static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
-{
- sljit_uw imm;
+ SLJIT_ASSERT (arg & SLJIT_MEM);
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg);
- if (arg & SLJIT_IMM) {
- imm = get_imm(argw);
- if (imm) {
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, imm)));
- return -1;
+ if ((arg & REG_MASK) == SLJIT_UNUSED) {
+ if (is_type1_transfer) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~0xfff));
+ argw &= 0xfff;
}
- imm = get_imm(~argw);
- if (imm) {
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, imm)));
- return -1;
+ else {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~0xff));
+ argw &= 0xff;
}
- return 0;
- }
-
- SLJIT_ASSERT(arg & SLJIT_MEM);
- /* Fast loads/stores. */
- if (!(arg & REG_MASK))
- return 0;
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg,
+ is_type1_transfer ? argw : TYPE2_TRANSFER_IMM(argw)));
+ }
if (arg & OFFS_REG_MASK) {
- if ((argw & 0x3) != 0 && !IS_TYPE1_TRANSFER(inp_flags))
- return 0;
-
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK,
- RM(OFFS_REG(arg)) | (IS_TYPE1_TRANSFER(inp_flags) ? SRC2_IMM : 0) | ((argw & 0x3) << 7))));
- return -1;
- }
+ offset_reg = OFFS_REG(arg);
+ arg &= REG_MASK;
+ argw &= 0x3;
- if (IS_TYPE1_TRANSFER(inp_flags)) {
- if (argw >= 0 && argw <= 0xfff) {
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, argw)));
- return -1;
- }
- if (argw < 0 && argw >= -0xfff) {
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, -argw)));
- return -1;
- }
- }
- else {
- if (argw >= 0 && argw <= 0xff) {
- if (inp_flags & ARG_TEST)
- return 1;
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw))));
- return -1;
- }
- if (argw < 0 && argw >= -0xff) {
- if (inp_flags & ARG_TEST)
- return 1;
- argw = -argw;
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw))));
- return -1;
+ if (argw != 0 && !is_type1_transfer) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | RM(offset_reg) | (argw << 7)));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, TYPE2_TRANSFER_IMM(0)));
}
- }
-
- return 0;
-}
-/* See getput_arg below.
- Note: can_cache is called only for binary operators. Those
- operators always uses word arguments without write back. */
-static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- /* Immediate caching is not supported as it would be an operation on constant arguments. */
- if (arg & SLJIT_IMM)
- return 0;
-
- /* Always a simple operation. */
- if (arg & OFFS_REG_MASK)
- return 0;
-
- if (!(arg & REG_MASK)) {
- /* Immediate access. */
- if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff))
- return 1;
- return 0;
+ /* Bit 25: RM is offset. */
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(offset_reg) | (is_type1_transfer ? (1 << 25) : 0) | (argw << 7)));
}
- if (argw <= 0xfffff && argw >= -0xfffff)
- return 0;
+ arg &= REG_MASK;
- if (argw == next_argw && (next_arg & SLJIT_MEM))
- return 1;
-
- if (arg == next_arg && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff))
- return 1;
-
- return 0;
-}
+ if (is_type1_transfer) {
+ if (argw > 0xfff) {
+ imm = get_imm(argw & ~0xfff);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm));
+ argw = argw & 0xfff;
+ arg = tmp_reg;
+ }
+ }
+ else if (argw < -0xfff) {
+ imm = get_imm(-argw & ~0xfff);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm));
+ argw = -(-argw & 0xfff);
+ arg = tmp_reg;
+ }
+ }
-#define GETPUT_ARG_DATA_TRANSFER(add, wb, target, base, imm) \
- if (max_delta & 0xf00) \
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, imm))); \
- else \
- FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, TYPE2_TRANSFER_IMM(imm))));
-
-#define TEST_WRITE_BACK() \
- if (inp_flags & WRITE_BACK) { \
- tmp_r = arg & REG_MASK; \
- if (reg == tmp_r) { \
- /* This can only happen for stores */ \
- /* since ldr reg, [reg, ...]! has no meaning */ \
- SLJIT_ASSERT(!(inp_flags & LOAD_DATA)); \
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(reg)))); \
- reg = TMP_REG3; \
- } \
- }
-
-/* Emit the necessary instructions. See can_cache above. */
-static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_s32 tmp_r;
- sljit_sw max_delta;
- sljit_sw sign;
- sljit_uw imm;
+ if (argw >= 0 && argw <= 0xfff)
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw));
- if (arg & SLJIT_IMM) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- return load_immediate(compiler, reg, argw);
+ if (argw < 0 && argw >= -0xfff)
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, -argw));
}
-
- SLJIT_ASSERT(arg & SLJIT_MEM);
-
- tmp_r = (inp_flags & LOAD_DATA) ? reg : TMP_REG3;
- max_delta = IS_TYPE1_TRANSFER(inp_flags) ? 0xfff : 0xff;
-
- if ((arg & REG_MASK) == SLJIT_UNUSED) {
- /* Write back is not used. */
- imm = (sljit_uw)(argw - compiler->cache_argw);
- if ((compiler->cache_arg & SLJIT_IMM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
- if (imm <= (sljit_uw)max_delta) {
- sign = 1;
- argw = argw - compiler->cache_argw;
+ else {
+ if (argw > 0xff) {
+ imm = get_imm(argw & ~0xff);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm));
+ argw = argw & 0xff;
+ arg = tmp_reg;
}
- else {
- sign = 0;
- argw = compiler->cache_argw - argw;
+ }
+ else if (argw < -0xff) {
+ imm = get_imm(-argw & ~0xff);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm));
+ argw = -(-argw & 0xff);
+ arg = tmp_reg;
}
-
- GETPUT_ARG_DATA_TRANSFER(sign, 0, reg, TMP_REG3, argw);
- return SLJIT_SUCCESS;
}
- /* With write back, we can create some sophisticated loads, but
- it is hard to decide whether we should convert downward (0s) or upward (1s). */
- imm = (sljit_uw)(argw - next_argw);
- if ((next_arg & SLJIT_MEM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
+ if (argw >= 0 && argw <= 0xff)
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, TYPE2_TRANSFER_IMM(argw)));
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
- tmp_r = TMP_REG3;
+ if (argw < 0 && argw >= -0xff) {
+ argw = -argw;
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, TYPE2_TRANSFER_IMM(argw)));
}
-
- FAIL_IF(load_immediate(compiler, tmp_r, argw));
- GETPUT_ARG_DATA_TRANSFER(1, 0, reg, tmp_r, 0);
- return SLJIT_SUCCESS;
- }
-
- if (arg & OFFS_REG_MASK) {
- SLJIT_ASSERT((argw & 0x3) && !(max_delta & 0xf00));
- if (inp_flags & WRITE_BACK)
- tmp_r = arg & REG_MASK;
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7))));
- return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, 0, reg, tmp_r, TYPE2_TRANSFER_IMM(0)));
- }
-
- imm = (sljit_uw)(argw - compiler->cache_argw);
- if (compiler->cache_arg == arg && imm <= (sljit_uw)max_delta) {
- SLJIT_ASSERT(!(inp_flags & WRITE_BACK));
- GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, imm);
- return SLJIT_SUCCESS;
}
- if (compiler->cache_arg == arg && imm >= (sljit_uw)-max_delta) {
- SLJIT_ASSERT(!(inp_flags & WRITE_BACK));
- imm = (sljit_uw)-(sljit_sw)imm;
- GETPUT_ARG_DATA_TRANSFER(0, 0, reg, TMP_REG3, imm);
- return SLJIT_SUCCESS;
- }
-
- imm = get_imm(argw & ~max_delta);
- if (imm) {
- TEST_WRITE_BACK();
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, imm)));
- GETPUT_ARG_DATA_TRANSFER(1, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta);
- return SLJIT_SUCCESS;
- }
-
- imm = get_imm(-argw & ~max_delta);
- if (imm) {
- argw = -argw;
- TEST_WRITE_BACK();
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, tmp_r, arg & REG_MASK, imm)));
- GETPUT_ARG_DATA_TRANSFER(0, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta);
- return SLJIT_SUCCESS;
- }
-
- if ((compiler->cache_arg & SLJIT_IMM) && compiler->cache_argw == argw) {
- TEST_WRITE_BACK();
- return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0)));
- }
-
- if (argw == next_argw && (next_arg & SLJIT_MEM)) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
-
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
-
- TEST_WRITE_BACK();
- return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0)));
- }
-
- imm = (sljit_uw)(argw - next_argw);
- if (arg == next_arg && !(inp_flags & WRITE_BACK) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, TMP_REG3, reg_map[arg & REG_MASK])));
-
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
- GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, 0);
- return SLJIT_SUCCESS;
- }
-
- if ((arg & REG_MASK) == tmp_r) {
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
- tmp_r = TMP_REG3;
- }
-
- FAIL_IF(load_immediate(compiler, tmp_r, argw));
- return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, reg_map[tmp_r] | (max_delta & 0xf00 ? SRC2_IMM : 0)));
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
-{
- if (getput_arg_fast(compiler, flags, reg, arg, argw))
- return compiler->error;
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- return getput_arg(compiler, flags, reg, arg, argw, 0, 0);
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
-{
- if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
- return compiler->error;
- return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(tmp_reg) | (is_type1_transfer ? (1 << 25) : 0)));
}
static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
@@ -1621,68 +1432,66 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- /* arg1 goes to TMP_REG1 or src reg
- arg2 goes to TMP_REG2, imm or src reg
- TMP_REG3 can be used for caching
- result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
+ /* src1 is reg or TMP_REG1
+ src2 is reg, TMP_REG2, or imm
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
/* We prefers register and simple consts. */
- sljit_s32 dst_r;
- sljit_s32 src1_r;
- sljit_s32 src2_r = 0;
- sljit_s32 sugg_src2_r = TMP_REG2;
- sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0;
-
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
+ sljit_s32 dst_reg;
+ sljit_s32 src1_reg;
+ sljit_s32 src2_reg;
+ sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
/* Destination check. */
- if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM))
- return SLJIT_SUCCESS;
- dst_r = TMP_REG2;
- }
- else if (FAST_IS_REG(dst)) {
- dst_r = dst;
- flags |= REG_DEST;
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
- sugg_src2_r = dst_r;
- }
- else {
- SLJIT_ASSERT(dst & SLJIT_MEM);
- if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) {
- flags |= FAST_DEST;
- dst_r = TMP_REG2;
- }
- else {
- flags |= SLOW_DEST;
- dst_r = 0;
+ if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED))
+ flags |= UNUSED_RETURN;
+
+ SLJIT_ASSERT(!(inp_flags & ALLOW_INV_IMM) || (inp_flags & ALLOW_IMM));
+
+ src2_reg = 0;
+
+ do {
+ if (!(inp_flags & ALLOW_IMM))
+ break;
+
+ if (src2 & SLJIT_IMM) {
+ src2_reg = get_imm(src2w);
+ if (src2_reg)
+ break;
+ if (inp_flags & ALLOW_INV_IMM) {
+ src2_reg = get_imm(~src2w);
+ if (src2_reg) {
+ flags |= INV_IMM;
+ break;
+ }
+ }
+ if (GET_OPCODE(op) == SLJIT_ADD) {
+ src2_reg = get_imm(-src2w);
+ if (src2_reg) {
+ op = SLJIT_SUB | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
+ if (GET_OPCODE(op) == SLJIT_SUB) {
+ src2_reg = get_imm(-src2w);
+ if (src2_reg) {
+ op = SLJIT_ADD | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
}
- }
- /* Source 1. */
- if (FAST_IS_REG(src1))
- src1_r = src1;
- else if (FAST_IS_REG(src2)) {
- flags |= ARGS_SWAPPED;
- src1_r = src2;
- src2 = src1;
- src2w = src1w;
- }
- else do { /* do { } while(0) is used because of breaks. */
- src1_r = 0;
- if ((inp_flags & ALLOW_ANY_IMM) && (src1 & SLJIT_IMM)) {
- /* The second check will generate a hit. */
- src2_r = get_imm(src1w);
- if (src2_r) {
+ if (src1 & SLJIT_IMM) {
+ src2_reg = get_imm(src1w);
+ if (src2_reg) {
flags |= ARGS_SWAPPED;
src1 = src2;
src1w = src2w;
break;
}
if (inp_flags & ALLOW_INV_IMM) {
- src2_r = get_imm(~src1w);
- if (src2_r) {
+ src2_reg = get_imm(~src1w);
+ if (src2_reg) {
flags |= ARGS_SWAPPED | INV_IMM;
src1 = src2;
src1w = src2w;
@@ -1690,9 +1499,9 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
}
}
if (GET_OPCODE(op) == SLJIT_ADD) {
- src2_r = get_imm(-src1w);
- if (src2_r) {
- /* Note: ARGS_SWAPPED is intentionally not applied! */
+ src2_reg = get_imm(-src1w);
+ if (src2_reg) {
+ /* Note: add is commutative operation. */
src1 = src2;
src1w = src2w;
op = SLJIT_SUB | GET_ALL_FLAGS(op);
@@ -1700,110 +1509,54 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
}
}
}
+ } while(0);
- if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) {
- FAIL_IF(compiler->error);
- src1_r = TMP_REG1;
- }
- } while (0);
+ /* Source 1. */
+ if (FAST_IS_REG(src1))
+ src1_reg = src1;
+ else if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1_reg = TMP_REG1;
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1_reg = TMP_REG1;
+ }
- /* Source 2. */
- if (src2_r == 0) {
- if (FAST_IS_REG(src2)) {
- src2_r = src2;
- flags |= REG_SOURCE;
- if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
- dst_r = src2_r;
- }
- else do { /* do { } while(0) is used because of breaks. */
- if ((inp_flags & ALLOW_ANY_IMM) && (src2 & SLJIT_IMM)) {
- src2_r = get_imm(src2w);
- if (src2_r)
- break;
- if (inp_flags & ALLOW_INV_IMM) {
- src2_r = get_imm(~src2w);
- if (src2_r) {
- flags |= INV_IMM;
- break;
- }
- }
- if (GET_OPCODE(op) == SLJIT_ADD) {
- src2_r = get_imm(-src2w);
- if (src2_r) {
- op = SLJIT_SUB | GET_ALL_FLAGS(op);
- flags &= ~ARGS_SWAPPED;
- break;
- }
- }
- if (GET_OPCODE(op) == SLJIT_SUB && !(flags & ARGS_SWAPPED)) {
- src2_r = get_imm(-src2w);
- if (src2_r) {
- op = SLJIT_ADD | GET_ALL_FLAGS(op);
- flags &= ~ARGS_SWAPPED;
- break;
- }
- }
- }
+ /* Destination. */
+ dst_reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;
- /* src2_r is 0. */
- if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) {
- FAIL_IF(compiler->error);
- src2_r = sugg_src2_r;
- }
- } while (0);
- }
+ if (op <= SLJIT_MOV_P) {
+ if (dst & SLJIT_MEM) {
+ if (inp_flags & BYTE_SIZE)
+ inp_flags &= ~SIGNED;
- /* src1_r, src2_r and dst_r can be zero (=unprocessed) or non-zero.
- If they are zero, they must not be registers. */
- if (src1_r == 0 && src2_r == 0 && dst_r == 0) {
- if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- SLJIT_ASSERT(!(flags & ARGS_SWAPPED));
- flags |= ARGS_SWAPPED;
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src1, src1w, dst, dstw));
+ if (FAST_IS_REG(src2))
+ return emit_op_mem(compiler, inp_flags, src2, dst, dstw, TMP_REG2);
}
- else {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
- }
- src1_r = TMP_REG1;
- src2_r = TMP_REG2;
- }
- else if (src1_r == 0 && src2_r == 0) {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
- src1_r = TMP_REG1;
- }
- else if (src1_r == 0 && dst_r == 0) {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
- src1_r = TMP_REG1;
- }
- else if (src2_r == 0 && dst_r == 0) {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
- src2_r = sugg_src2_r;
+
+ if (FAST_IS_REG(src2) && dst_reg != TMP_REG2)
+ flags |= MOVE_REG_CONV;
}
- if (dst_r == 0)
- dst_r = TMP_REG2;
+ /* Source 2. */
+ if (src2_reg == 0) {
+ src2_reg = (op <= SLJIT_MOV_P) ? dst_reg : TMP_REG2;
- if (src1_r == 0) {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0));
- src1_r = TMP_REG1;
+ if (FAST_IS_REG(src2))
+ src2_reg = src2;
+ else if (src2 & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, src2_reg, src2, src2w, TMP_REG2));
+ else
+ FAIL_IF(load_immediate(compiler, src2_reg, src2w));
}
- if (src2_r == 0) {
- FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0));
- src2_r = sugg_src2_r;
- }
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_reg, src1_reg, src2_reg));
- FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
- if (flags & (FAST_DEST | SLOW_DEST)) {
- if (flags & FAST_DEST)
- FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw));
- else
- FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0));
- }
- return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, inp_flags, dst_reg, dst, dstw, TMP_REG1);
}
#ifdef __cplusplus
@@ -1823,6 +1576,9 @@ extern int __aeabi_idivmod(int numerator, int denominator);
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
{
+ sljit_sw saved_reg_list[3];
+ sljit_sw saved_reg_count;
+
CHECK_ERROR();
CHECK(check_sljit_emit_op0(compiler, op));
@@ -1836,33 +1592,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
break;
case SLJIT_LMUL_UW:
case SLJIT_LMUL_SW:
-#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
| (reg_map[SLJIT_R1] << 16)
| (reg_map[SLJIT_R0] << 12)
| (reg_map[SLJIT_R0] << 8)
| reg_map[SLJIT_R1]);
-#else
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, RM(SLJIT_R1))));
- return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
- | (reg_map[SLJIT_R1] << 16)
- | (reg_map[SLJIT_R0] << 12)
- | (reg_map[SLJIT_R0] << 8)
- | reg_map[TMP_REG1]);
-#endif
case SLJIT_DIVMOD_UW:
case SLJIT_DIVMOD_SW:
case SLJIT_DIV_UW:
case SLJIT_DIV_SW:
SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
- SLJIT_COMPILE_ASSERT(reg_map[2] == 1 && reg_map[3] == 2, bad_register_mapping);
-
- if ((op >= SLJIT_DIV_UW) && (compiler->scratches >= 3)) {
- FAIL_IF(push_inst(compiler, 0xe52d2008 /* str r2, [sp, #-8]! */));
- FAIL_IF(push_inst(compiler, 0xe58d1004 /* str r1, [sp, #4] */));
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
+
+ saved_reg_count = 0;
+ if (compiler->scratches >= 4)
+ saved_reg_list[saved_reg_count++] = 3;
+ if (compiler->scratches >= 3)
+ saved_reg_list[saved_reg_count++] = 2;
+ if (op >= SLJIT_DIV_UW)
+ saved_reg_list[saved_reg_count++] = 1;
+
+ if (saved_reg_count > 0) {
+ FAIL_IF(push_inst(compiler, 0xe52d0000 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */));
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, 0xe58d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */));
+ }
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, 0xe58d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */));
+ }
}
- else if ((op >= SLJIT_DIV_UW) || (compiler->scratches >= 3))
- FAIL_IF(push_inst(compiler, 0xe52d0008 | (op >= SLJIT_DIV_UW ? 0x1000 : 0x2000) /* str r1/r2, [sp, #-8]! */));
#if defined(__GNUC__)
FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
@@ -1871,12 +1632,18 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
#error "Software divmod functions are needed"
#endif
- if ((op >= SLJIT_DIV_UW) && (compiler->scratches >= 3)) {
- FAIL_IF(push_inst(compiler, 0xe59d1004 /* ldr r1, [sp, #4] */));
- FAIL_IF(push_inst(compiler, 0xe49d2008 /* ldr r2, [sp], #8 */));
+ if (saved_reg_count > 0) {
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, 0xe59d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */));
+ }
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, 0xe59d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */));
+ }
+ return push_inst(compiler, 0xe49d0000 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */);
}
- else if ((op >= SLJIT_DIV_UW) || (compiler->scratches >= 3))
- return push_inst(compiler, 0xe49d0008 | (op >= SLJIT_DIV_UW ? 0x1000 : 0x2000) /* ldr r1/r2, [sp], #8 */);
return SLJIT_SUCCESS;
}
@@ -1892,6 +1659,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM))
+ return emit_op_mem(compiler, PRELOAD | LOAD_DATA, TMP_PC, src, srcw, TMP_REG1);
+#endif
+ return SLJIT_SUCCESS;
+ }
+
switch (GET_OPCODE(op)) {
case SLJIT_MOV:
case SLJIT_MOV_U32:
@@ -1900,34 +1675,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
case SLJIT_MOV_U8:
- return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+ return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
case SLJIT_MOV_S8:
- return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+ return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
case SLJIT_MOV_U16:
- return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+ return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
case SLJIT_MOV_S16:
- return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
-
- case SLJIT_MOVU:
- case SLJIT_MOVU_U32:
- case SLJIT_MOVU_S32:
- case SLJIT_MOVU_P:
- return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
- case SLJIT_MOVU_U8:
- return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
-
- case SLJIT_MOVU_S8:
- return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
-
- case SLJIT_MOVU_U16:
- return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
-
- case SLJIT_MOVU_S16:
- return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+ return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
case SLJIT_NOT:
return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
@@ -1957,6 +1714,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
+
switch (GET_OPCODE(op)) {
case SLJIT_ADD:
case SLJIT_ADDC:
@@ -1997,7 +1757,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg << 1;
+ return (freg_map[reg] << 1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -2013,118 +1773,63 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
-
-/* 0 - no fpu
- 1 - vfp */
-static sljit_s32 arm_fpu_type = -1;
-
-static void init_compiler(void)
-{
- if (arm_fpu_type != -1)
- return;
-
- /* TODO: Only the OS can help to determine the correct fpu type. */
- arm_fpu_type = 1;
-}
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#else
- if (arm_fpu_type == -1)
- init_compiler();
- return arm_fpu_type;
-#endif
-}
-
-#else
-
-#define arm_fpu_type 1
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
- /* Always available. */
- return 1;
-}
-
-#endif
#define FPU_LOAD (1 << 20)
#define EMIT_FPU_DATA_TRANSFER(inst, add, base, freg, offs) \
- ((inst) | ((add) << 23) | (reg_map[base] << 16) | (freg << 12) | (offs))
+ ((inst) | ((add) << 23) | (reg_map[base] << 16) | (freg_map[freg] << 12) | (offs))
#define EMIT_FPU_OPERATION(opcode, mode, dst, src1, src2) \
- ((opcode) | (mode) | ((dst) << 12) | (src1) | ((src2) << 16))
+ ((opcode) | (mode) | (freg_map[dst] << 12) | freg_map[src1] | (freg_map[src2] << 16))
static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
{
- sljit_sw tmp;
sljit_uw imm;
sljit_sw inst = VSTR_F32 | (flags & (SLJIT_F32_OP | FPU_LOAD));
+
SLJIT_ASSERT(arg & SLJIT_MEM);
+ arg &= ~SLJIT_MEM;
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7))));
- arg = SLJIT_MEM | TMP_REG1;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((argw & 0x3) << 7)));
+ arg = TMP_REG2;
argw = 0;
}
/* Fast loads and stores. */
- if ((arg & REG_MASK)) {
+ if (arg) {
if (!(argw & ~0x3fc))
return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, arg & REG_MASK, reg, argw >> 2));
if (!(-argw & ~0x3fc))
return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, arg & REG_MASK, reg, (-argw) >> 2));
- }
-
- if (compiler->cache_arg == arg) {
- tmp = argw - compiler->cache_argw;
- if (!(tmp & ~0x3fc))
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, tmp >> 2));
- if (!(-tmp & ~0x3fc))
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG3, reg, -tmp >> 2));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0));
- }
- }
- if (arg & REG_MASK) {
- if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, 0));
- }
imm = get_imm(argw & ~0x3fc);
if (imm) {
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, imm)));
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, (argw & 0x3fc) >> 2));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, (argw & 0x3fc) >> 2));
}
imm = get_imm(-argw & ~0x3fc);
if (imm) {
argw = -argw;
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, TMP_REG1, arg & REG_MASK, imm)));
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG1, reg, (argw & 0x3fc) >> 2));
+ FAIL_IF(push_inst(compiler, SUB | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG2, reg, (argw & 0x3fc) >> 2));
}
}
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
- if (arg & REG_MASK) {
- FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, arg & REG_MASK, reg_map[TMP_REG1])));
+ if (arg) {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, argw));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(TMP_REG2)));
}
else
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
+ FAIL_IF(load_immediate(compiler, TMP_REG2, argw));
- return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, 0));
}
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
+ op ^= SLJIT_F32_OP;
+
if (src & SLJIT_MEM) {
FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src, srcw));
src = TMP_FREG1;
@@ -2132,11 +1837,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_S32_F32, op & SLJIT_F32_OP, TMP_FREG1, src, 0)));
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
- return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | (TMP_FREG1 << 16));
+ return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | (freg_map[TMP_FREG1] << 16));
/* Store the integer value from a VFP register. */
return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
@@ -2148,15 +1850,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
{
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ op ^= SLJIT_F32_OP;
+
if (FAST_IS_REG(src))
- FAIL_IF(push_inst(compiler, VMOV | RD(src) | (TMP_FREG1 << 16)));
+ FAIL_IF(push_inst(compiler, VMOV | RD(src) | (freg_map[TMP_FREG1] << 16)));
else if (src & SLJIT_MEM) {
/* Load the integer value into a VFP register. */
FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
}
else {
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
- FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | (TMP_FREG1 << 16)));
+ FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | (freg_map[TMP_FREG1] << 16)));
}
FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F32_S32, op & SLJIT_F32_OP, dst_r, TMP_FREG1, 0)));
@@ -2170,6 +1874,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
+ op ^= SLJIT_F32_OP;
+
if (src1 & SLJIT_MEM) {
FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src1, src1w));
src1 = TMP_FREG1;
@@ -2191,16 +1897,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
sljit_s32 dst_r;
CHECK_ERROR();
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
- op ^= SLJIT_F32_OP;
SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100), float_transfer_bit_error);
SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_F32_OP;
+
if (src & SLJIT_MEM) {
FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, dst_r, src, srcw));
src = dst_r;
@@ -2245,8 +1950,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
op ^= SLJIT_F32_OP;
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
@@ -2287,7 +1990,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
#undef FPU_LOAD
#undef EMIT_FPU_DATA_TRANSFER
-#undef EMIT_FPU_OPERATION
/* --------------------------------------------------------------------- */
/* Other instructions */
@@ -2299,21 +2001,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
if (FAST_IS_REG(dst))
- return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, RM(TMP_REG3)));
+ return push_inst(compiler, MOV | RD(dst) | RM(TMP_REG2));
/* Memory. */
- if (getput_arg_fast(compiler, WORD_DATA, TMP_REG3, dst, dstw))
- return compiler->error;
- /* TMP_REG3 is used for caching. */
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG2, SLJIT_UNUSED, RM(TMP_REG3))));
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- return getput_arg(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0);
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
@@ -2322,21 +2016,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
CHECK(check_sljit_emit_fast_return(compiler, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
if (FAST_IS_REG(src))
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(src))));
- else if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, WORD_DATA | LOAD_DATA, TMP_REG3, src, srcw))
- FAIL_IF(compiler->error);
- else {
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- FAIL_IF(getput_arg(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, 0, 0));
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(TMP_REG2))));
- }
- }
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, TMP_REG3, srcw));
- return push_inst(compiler, BLX | RM(TMP_REG3));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG1));
+
+ return push_inst(compiler, BX | RM(TMP_REG2));
}
/* --------------------------------------------------------------------- */
@@ -2393,7 +2080,7 @@ static sljit_uw get_cc(sljit_s32 type)
return 0x70000000;
default:
- SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3);
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL);
return 0xe0000000;
}
}
@@ -2426,11 +2113,12 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
type &= 0xff;
- /* In ARM, we don't need to touch the arguments. */
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
if (type >= SLJIT_FAST_CALL)
PTR_FAIL_IF(prepare_blx(compiler));
- PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0,
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(type), 0));
if (jump->flags & SLJIT_REWRITABLE_JUMP) {
@@ -2455,6 +2143,241 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
return jump;
}
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 stack_offset = 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_offset = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u8 offsets[4];
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_f32);
+ arg_count++;
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_offset & 0x7)
+ stack_offset += sizeof(sljit_sw);
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_f64);
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_sw);
+ arg_count++;
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (stack_offset > 16)
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | (((stack_offset - 16) + 0x7) & ~0x7)));
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ float_arg_count--;
+ stack_offset = offsets[arg_count];
+
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (stack_offset << 10)));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800000 | RN(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ arg_count--;
+ float_arg_count--;
+ stack_offset = offsets[arg_count];
+
+ SLJIT_ASSERT((stack_offset & 0x7) == 0);
+
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset || src_offset == stack_offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV2 | 0x100000 | (stack_offset << 10) | ((stack_offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800100 | RN(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2)));
+ break;
+ default:
+ arg_count--;
+ word_arg_offset -= sizeof(sljit_sw);
+ stack_offset = offsets[arg_count];
+
+ SLJIT_ASSERT(stack_offset >= word_arg_offset);
+
+ if (stack_offset != word_arg_offset) {
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = 1 + (stack_offset >> 2);
+ src_offset = stack_offset;
+ }
+ FAIL_IF(push_inst(compiler, MOV | (stack_offset << 10) | (word_arg_offset >> 2)));
+ } else
+ FAIL_IF(push_inst(compiler, data_transfer_insts[WORD_SIZE] | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (stack_offset - 16)));
+ }
+ break;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_s32 stack_size = 0;
+
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst(compiler, VMOV | (0 << 16) | (0 << 12)));
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_size & 0x7)
+ stack_size += sizeof(sljit_sw);
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ stack_size += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (stack_size <= 16)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | (((stack_size - 16) + 0x7) & ~0x7));
+}
+
+#else /* !__SOFTFP__ */
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 remap = 0;
+ sljit_u32 offset = 0;
+ sljit_u32 new_offset, mask;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) {
+ new_offset = 0;
+ mask = 1;
+
+ while (remap & mask) {
+ new_offset++;
+ mask <<= 1;
+ }
+ remap |= mask;
+
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ 0, (new_offset >> 1) + 1, (offset >> 1) + 1, 0) | ((new_offset & 0x1) ? 0x400000 : 0)));
+
+ offset += 2;
+ }
+ else if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) {
+ new_offset = 0;
+ mask = 3;
+
+ while (remap & mask) {
+ new_offset += 2;
+ mask <<= 2;
+ }
+ remap |= mask;
+
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, SLJIT_F32_OP, (new_offset >> 1) + 1, (offset >> 1) + 1, 0)));
+
+ offset += 2;
+ }
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif /* __SOFTFP__ */
+
+#undef EMIT_FPU_OPERATION
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+#else /* !__SOFTFP__ */
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+#endif /* __SOFTFP__ */
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
{
struct sljit_jump *jump;
@@ -2463,16 +2386,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
- /* In ARM, we don't need to touch the arguments. */
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
if (!(src & SLJIT_IMM)) {
- if (FAST_IS_REG(src))
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src));
+ }
SLJIT_ASSERT(src & SLJIT_MEM);
- FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw));
- return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG2));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1));
}
+ /* These jumps are converted to jump/call instructions when possible. */
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF(!jump);
set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
@@ -2481,7 +2408,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
if (type >= SLJIT_FAST_CALL)
FAIL_IF(prepare_blx(compiler));
- FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0));
+ FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0));
if (type >= SLJIT_FAST_CALL)
FAIL_IF(emit_blx(compiler));
#else
@@ -2492,57 +2419,221 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
return SLJIT_SUCCESS;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+#ifdef __SOFTFP__
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ return softfloat_post_call_with_args(compiler, arg_types);
+#else /* !__SOFTFP__ */
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_ijump(compiler, type, src, srcw);
+#endif /* __SOFTFP__ */
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
- sljit_s32 dst_r, flags = GET_ALL_FLAGS(op);
+ sljit_s32 dst_reg, flags = GET_ALL_FLAGS(op);
sljit_uw cc, ins;
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- ADJUST_LOCAL_OFFSET(src, srcw);
-
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
op = GET_OPCODE(op);
cc = get_cc(type & 0xff);
- dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
if (op < SLJIT_ADD) {
- FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 0)));
- FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 1) & ~COND_MASK) | cc));
- return (dst_r == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS;
+ FAIL_IF(push_inst(compiler, MOV | RD(dst_reg) | SRC2_IMM | 0));
+ FAIL_IF(push_inst(compiler, ((MOV | RD(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
}
- ins = (op == SLJIT_AND ? AND_DP : (op == SLJIT_OR ? ORR_DP : EOR_DP));
- if ((op == SLJIT_OR || op == SLJIT_XOR) && FAST_IS_REG(dst) && dst == src) {
- FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst, dst, SRC2_IMM | 1) & ~COND_MASK) | cc));
- /* The condition must always be set, even if the ORR/EOR is not executed above. */
- return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst))) : SLJIT_SUCCESS;
- }
+ ins = (op == SLJIT_AND ? AND : (op == SLJIT_OR ? ORR : EOR));
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
- src = TMP_REG1;
- srcw = 0;
- } else if (src & SLJIT_IMM) {
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG2));
+
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+
+ if (op == SLJIT_AND)
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000)));
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (flags & SLJIT_SET_Z)
+ return push_inst(compiler, MOV | SET_FLAGS | RD(TMP_REG2) | RM(dst_reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ dst_reg &= ~SLJIT_I32_OP;
+
+ cc = get_cc(type & 0xff);
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ tmp = get_imm(srcw);
+ if (tmp)
+ return push_inst(compiler, ((MOV | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+ tmp = get_imm(~srcw);
+ if (tmp)
+ return push_inst(compiler, ((MVN | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ tmp = (sljit_uw) srcw;
+ FAIL_IF(push_inst(compiler, (MOVW & ~COND_MASK) | cc | RD(dst_reg) | ((tmp << 4) & 0xf0000) | (tmp & 0xfff)));
+ if (tmp <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, (MOVT & ~COND_MASK) | cc | RD(dst_reg) | ((tmp >> 12) & 0xf0000) | ((tmp >> 16) & 0xfff));
+#else
FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
src = TMP_REG1;
- srcw = 0;
+#endif
}
- FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 1) & ~COND_MASK) | cc));
- FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000)));
- if (dst_r == TMP_REG2)
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0));
+ return push_inst(compiler, ((MOV | RD(dst_reg) | RM(src)) & ~COND_MASK) | cc);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_uw is_type1_transfer, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ is_type1_transfer = 1;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ if (!(type & SLJIT_MEM_STORE))
+ is_type1_transfer = 0;
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ SLJIT_ASSERT(is_type1_transfer == !!IS_TYPE1_TRANSFER(flags));
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (!is_type1_transfer && memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ else {
+ if (is_type1_transfer) {
+ if (memw > 4095 && memw < -4095)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ else {
+ if (memw > 255 && memw < -255)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ inst = EMIT_DATA_TRANSFER(flags, 1, reg, mem & REG_MASK, RM(OFFS_REG(mem)) | (memw << 7));
+
+ if (is_type1_transfer)
+ inst |= (1 << 25);
+
+ if (type & SLJIT_MEM_PRE)
+ inst |= (1 << 21);
+ else
+ inst ^= (1 << 24);
+
+ return push_inst(compiler, inst);
+ }
+
+ inst = EMIT_DATA_TRANSFER(flags, 0, reg, mem & REG_MASK, 0);
+
+ if (type & SLJIT_MEM_PRE)
+ inst |= (1 << 21);
+ else
+ inst ^= (1 << 24);
+
+ if (is_type1_transfer) {
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
+
+ return push_inst(compiler, inst | memw);
+ }
+
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
- return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst_r))) : SLJIT_SUCCESS;
+ return push_inst(compiler, inst | TYPE2_TRANSFER_IMM(memw));
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
@@ -2560,7 +2651,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
- PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), init_value));
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), init_value));
compiler->patches++;
#else
PTR_FAIL_IF(emit_imm(compiler, reg, init_value));
@@ -2568,7 +2659,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
set_const(const_, compiler);
if (dst & SLJIT_MEM)
- PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1));
return const_;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c
index 2062d80b0a..8a437bd6a0 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -36,15 +36,19 @@ typedef sljit_u32 sljit_ins;
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
-#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 5)
-#define TMP_SP (SLJIT_NUMBER_OF_REGISTERS + 6)
+#define TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 4)
+#define TMP_SP (SLJIT_NUMBER_OF_REGISTERS + 5)
-#define TMP_FREG1 (0)
-#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+/* r18 - platform register, currently not used */
static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
- 31, 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 29, 9, 10, 11, 30, 31
+ 31, 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 29, 9, 10, 30, 31
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7
};
#define W_OP (1 << 31)
@@ -53,10 +57,10 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
#define RN(rn) (reg_map[rn] << 5)
#define RT2(rt2) (reg_map[rt2] << 10)
#define RM(rm) (reg_map[rm] << 16)
-#define VD(vd) (vd)
-#define VT(vt) (vt)
-#define VN(vn) ((vn) << 5)
-#define VM(vm) ((vm) << 16)
+#define VD(vd) (freg_map[vd])
+#define VT(vt) (freg_map[vt])
+#define VN(vn) (freg_map[vn] << 5)
+#define VM(vm) (freg_map[vm] << 16)
/* --------------------------------------------------------------------- */
/* Instrucion forms */
@@ -76,6 +80,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
#define BRK 0xd4200000
#define CBZ 0xb4000000
#define CLZ 0xdac01000
+#define CSEL 0x9a800000
#define CSINC 0x9a800400
#define EOR 0xca000000
#define EORI 0xd2000000
@@ -111,10 +116,13 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
#define SMULH 0x9b403c00
#define STP 0xa9000000
#define STP_PRE 0xa9800000
+#define STRB 0x38206800
+#define STRBI 0x39000000
#define STRI 0xf9000000
#define STR_FI 0x3d000000
#define STR_FR 0x3c206800
#define STUR_FI 0x3c000000
+#define STURBI 0x38000000
#define SUB 0xcb000000
#define SUBI 0xd1000000
#define SUBS 0xeb000000
@@ -192,6 +200,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in
code_ptr[-2] = code_ptr[0];
return 2;
}
+
if (target_addr <= 0xffffffffffffl) {
if (jump->flags & IS_COND)
code_ptr[-5] -= (1 << 5);
@@ -323,6 +332,26 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return code;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Core code generator functions. */
/* --------------------------------------------------------------------- */
@@ -372,12 +401,14 @@ static sljit_ins logical_imm(sljit_sw imm, sljit_s32 len)
SLJIT_ASSERT((len == 32 && imm != 0 && imm != -1)
|| (len == 16 && (sljit_s32)imm != 0 && (sljit_s32)imm != -1));
+
uimm = (sljit_uw)imm;
while (1) {
if (len <= 0) {
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return 0;
}
+
mask = ((sljit_uw)1 << len) - 1;
if ((uimm & mask) != ((uimm >> len) & mask))
break;
@@ -426,39 +457,42 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst,
sljit_s32 i, zeros, ones, first;
sljit_ins bitmask;
+ /* Handling simple immediates first. */
if (imm <= 0xffff)
return push_inst(compiler, MOVZ | RD(dst) | (imm << 5));
- if (simm >= -0x10000 && simm < 0)
+ if (simm < 0 && simm >= -0x10000)
return push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff) << 5));
if (imm <= 0xffffffffl) {
+ if ((imm & 0xffff) == 0)
+ return push_inst(compiler, MOVZ | RD(dst) | ((imm >> 16) << 5) | (1 << 21));
if ((imm & 0xffff0000l) == 0xffff0000)
return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | ((~imm & 0xffff) << 5));
if ((imm & 0xffff) == 0xffff)
return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | ((~imm & 0xffff0000l) >> (16 - 5)) | (1 << 21));
+
bitmask = logical_imm(simm, 16);
if (bitmask != 0)
return push_inst(compiler, (ORRI ^ W_OP) | RD(dst) | RN(TMP_ZERO) | bitmask);
- }
- else {
- bitmask = logical_imm(simm, 32);
- if (bitmask != 0)
- return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask);
- }
- if (imm <= 0xffffffffl) {
FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | ((imm & 0xffff) << 5)));
return push_inst(compiler, MOVK | RD(dst) | ((imm & 0xffff0000l) >> (16 - 5)) | (1 << 21));
}
- if (simm >= -0x100000000l && simm < 0) {
+ bitmask = logical_imm(simm, 32);
+ if (bitmask != 0)
+ return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask);
+
+ if (simm < 0 && simm >= -0x100000000l) {
+ if ((imm & 0xffff) == 0xffff)
+ return push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff0000l) >> (16 - 5)) | (1 << 21));
+
FAIL_IF(push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff) << 5)));
return push_inst(compiler, MOVK | RD(dst) | ((imm & 0xffff0000l) >> (16 - 5)) | (1 << 21));
}
- /* A large amount of number can be constructed from ORR and MOVx,
- but computing them is costly. We don't */
+ /* A large amount of number can be constructed from ORR and MOVx, but computing them is costly. */
zeros = 0;
ones = 0;
@@ -511,9 +545,6 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst,
#define INT_OP 0x0040000
#define SET_FLAGS 0x0080000
#define UNUSED_RETURN 0x0100000
-#define SLOW_DEST 0x0200000
-#define SLOW_SRC1 0x0400000
-#define SLOW_SRC2 0x0800000
#define CHECK_FLAGS(flag_bits) \
if (flags & SET_FLAGS) { \
@@ -645,7 +676,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
}
goto set_flags;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -671,40 +702,32 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
switch (op) {
case SLJIT_MOV:
case SLJIT_MOV_P:
- case SLJIT_MOVU:
- case SLJIT_MOVU_P:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
if (dst == arg2)
return SLJIT_SUCCESS;
return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(arg2));
case SLJIT_MOV_U8:
- case SLJIT_MOVU_U8:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
return push_inst(compiler, (UBFM ^ (1 << 31)) | RD(dst) | RN(arg2) | (7 << 10));
case SLJIT_MOV_S8:
- case SLJIT_MOVU_S8:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
if (!(flags & INT_OP))
inv_bits |= 1 << 22;
return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (7 << 10));
case SLJIT_MOV_U16:
- case SLJIT_MOVU_U16:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
return push_inst(compiler, (UBFM ^ (1 << 31)) | RD(dst) | RN(arg2) | (15 << 10));
case SLJIT_MOV_S16:
- case SLJIT_MOVU_S16:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
if (!(flags & INT_OP))
inv_bits |= 1 << 22;
return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (15 << 10));
case SLJIT_MOV_U32:
- case SLJIT_MOVU_U32:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
if ((flags & INT_OP) && dst == arg2)
return SLJIT_SUCCESS;
return push_inst(compiler, (ORR ^ (1 << 31)) | RD(dst) | RN(TMP_ZERO) | RM(arg2));
case SLJIT_MOV_S32:
- case SLJIT_MOVU_S32:
SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
if ((flags & INT_OP) && dst == arg2)
return SLJIT_SUCCESS;
@@ -712,7 +735,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
case SLJIT_NOT:
SLJIT_ASSERT(arg1 == TMP_REG1);
FAIL_IF(push_inst(compiler, (ORN ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
case SLJIT_NEG:
SLJIT_ASSERT(arg1 == TMP_REG1);
if (flags & SET_FLAGS)
@@ -720,8 +743,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
return push_inst(compiler, (SUB ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2));
case SLJIT_CLZ:
SLJIT_ASSERT(arg1 == TMP_REG1);
- FAIL_IF(push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2)));
- goto set_flags;
+ return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2));
case SLJIT_ADD:
CHECK_FLAGS(1 << 29);
return push_inst(compiler, (ADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
@@ -750,320 +772,91 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
return push_inst(compiler, (AND ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
case SLJIT_OR:
FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
case SLJIT_XOR:
FAIL_IF(push_inst(compiler, (EOR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
case SLJIT_SHL:
FAIL_IF(push_inst(compiler, (LSLV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
case SLJIT_LSHR:
FAIL_IF(push_inst(compiler, (LSRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
case SLJIT_ASHR:
FAIL_IF(push_inst(compiler, (ASRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
- goto set_flags;
+ break; /* Set flags. */
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
}
- SLJIT_ASSERT_STOP();
- return SLJIT_SUCCESS;
-
set_flags:
if (flags & SET_FLAGS)
return push_inst(compiler, (SUBS ^ inv_bits) | RD(TMP_ZERO) | RN(dst) | RM(TMP_ZERO));
return SLJIT_SUCCESS;
}
-#define STORE 0x01
-#define SIGNED 0x02
-
-#define UPDATE 0x04
-#define ARG_TEST 0x08
-
-#define BYTE_SIZE 0x000
-#define HALF_SIZE 0x100
-#define INT_SIZE 0x200
-#define WORD_SIZE 0x300
-
-#define MEM_SIZE_SHIFT(flags) ((flags) >> 8)
-
-static const sljit_ins sljit_mem_imm[4] = {
-/* u l */ 0x39400000 /* ldrb [reg,imm] */,
-/* u s */ 0x39000000 /* strb [reg,imm] */,
-/* s l */ 0x39800000 /* ldrsb [reg,imm] */,
-/* s s */ 0x39000000 /* strb [reg,imm] */,
-};
-
-static const sljit_ins sljit_mem_simm[4] = {
-/* u l */ 0x38400000 /* ldurb [reg,imm] */,
-/* u s */ 0x38000000 /* sturb [reg,imm] */,
-/* s l */ 0x38800000 /* ldursb [reg,imm] */,
-/* s s */ 0x38000000 /* sturb [reg,imm] */,
-};
+#define STORE 0x10
+#define SIGNED 0x20
-static const sljit_ins sljit_mem_pre_simm[4] = {
-/* u l */ 0x38400c00 /* ldrb [reg,imm]! */,
-/* u s */ 0x38000c00 /* strb [reg,imm]! */,
-/* s l */ 0x38800c00 /* ldrsb [reg,imm]! */,
-/* s s */ 0x38000c00 /* strb [reg,imm]! */,
-};
-
-static const sljit_ins sljit_mem_reg[4] = {
-/* u l */ 0x38606800 /* ldrb [reg,reg] */,
-/* u s */ 0x38206800 /* strb [reg,reg] */,
-/* s l */ 0x38a06800 /* ldrsb [reg,reg] */,
-/* s s */ 0x38206800 /* strb [reg,reg] */,
-};
+#define BYTE_SIZE 0x0
+#define HALF_SIZE 0x1
+#define INT_SIZE 0x2
+#define WORD_SIZE 0x3
-/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
-static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value)
-{
- if (value >= 0) {
- if (value <= 0xfff)
- return push_inst(compiler, ADDI | RD(dst) | RN(reg) | (value << 10));
- if (value <= 0xffffff && !(value & 0xfff))
- return push_inst(compiler, ADDI | (1 << 22) | RD(dst) | RN(reg) | (value >> 2));
- }
- else {
- value = -value;
- if (value <= 0xfff)
- return push_inst(compiler, SUBI | RD(dst) | RN(reg) | (value << 10));
- if (value <= 0xffffff && !(value & 0xfff))
- return push_inst(compiler, SUBI | (1 << 22) | RD(dst) | RN(reg) | (value >> 2));
- }
- return SLJIT_ERR_UNSUPPORTED;
-}
+#define MEM_SIZE_SHIFT(flags) ((flags) & 0x3)
-/* Can perform an operation using at most 1 instruction. */
-static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
{
sljit_u32 shift = MEM_SIZE_SHIFT(flags);
+ sljit_u32 type = (shift << 30);
- SLJIT_ASSERT(arg & SLJIT_MEM);
-
- if (SLJIT_UNLIKELY(flags & UPDATE)) {
- if ((arg & REG_MASK) && !(arg & OFFS_REG_MASK) && argw <= 255 && argw >= -256) {
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
+ if (!(flags & STORE))
+ type |= (flags & SIGNED) ? 0x00800000 : 0x00400000;
- arg &= REG_MASK;
- argw &= 0x1ff;
- FAIL_IF(push_inst(compiler, sljit_mem_pre_simm[flags & 0x3]
- | (shift << 30) | RT(reg) | RN(arg) | (argw << 12)));
- return -1;
- }
- return 0;
- }
+ SLJIT_ASSERT(arg & SLJIT_MEM);
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
argw &= 0x3;
- if (argw && argw != shift)
- return 0;
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STRB | type | RT(reg)
+ | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
- FAIL_IF(push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg)
- | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0)));
- return -1;
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw << 10)));
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg));
}
arg &= REG_MASK;
- if (argw >= 0 && (argw >> shift) <= 0xfff && (argw & ((1 << shift) - 1)) == 0) {
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
- FAIL_IF(push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30)
- | RT(reg) | RN(arg) | (argw << (10 - shift))));
- return -1;
- }
+ if (arg == SLJIT_UNUSED) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~(0xfff << shift)));
- if (argw > 255 || argw < -256)
- return 0;
-
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
-
- FAIL_IF(push_inst(compiler, sljit_mem_simm[flags & 0x3] | (shift << 30)
- | RT(reg) | RN(arg) | ((argw & 0x1ff) << 12)));
- return -1;
-}
-
-/* see getput_arg below.
- Note: can_cache is called only for binary operators. Those
- operators always uses word arguments without write back. */
-static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_sw diff;
- if ((arg & OFFS_REG_MASK) || !(next_arg & SLJIT_MEM))
- return 0;
+ argw = (argw >> shift) & 0xfff;
- if (!(arg & REG_MASK)) {
- diff = argw - next_argw;
- if (diff <= 0xfff && diff >= -0xfff)
- return 1;
- return 0;
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | (argw << 10));
}
- if (argw == next_argw)
- return 1;
-
- diff = argw - next_argw;
- if (arg == next_arg && diff <= 0xfff && diff >= -0xfff)
- return 1;
-
- return 0;
-}
-
-/* Emit the necessary instructions. See can_cache above. */
-static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
- sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_u32 shift = MEM_SIZE_SHIFT(flags);
- sljit_s32 tmp_r, other_r;
- sljit_sw diff;
-
- SLJIT_ASSERT(arg & SLJIT_MEM);
- if (!(next_arg & SLJIT_MEM)) {
- next_arg = 0;
- next_argw = 0;
- }
-
- tmp_r = (flags & STORE) ? TMP_REG3 : reg;
-
- if (SLJIT_UNLIKELY((flags & UPDATE) && (arg & REG_MASK))) {
- /* Update only applies if a base register exists. */
- other_r = OFFS_REG(arg);
- if (!other_r) {
- other_r = arg & REG_MASK;
- if (other_r != reg && argw >= 0 && argw <= 0xffffff) {
- if ((argw & 0xfff) != 0)
- FAIL_IF(push_inst(compiler, ADDI | RD(other_r) | RN(other_r) | ((argw & 0xfff) << 10)));
- if (argw >> 12)
- FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(other_r) | RN(other_r) | ((argw >> 12) << 10)));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(other_r));
- }
- else if (other_r != reg && argw < 0 && argw >= -0xffffff) {
- argw = -argw;
- if ((argw & 0xfff) != 0)
- FAIL_IF(push_inst(compiler, SUBI | RD(other_r) | RN(other_r) | ((argw & 0xfff) << 10)));
- if (argw >> 12)
- FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(other_r) | RN(other_r) | ((argw >> 12) << 10)));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(other_r));
- }
-
- if (compiler->cache_arg == SLJIT_MEM) {
- if (argw == compiler->cache_argw) {
- other_r = TMP_REG3;
- argw = 0;
- }
- else if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- other_r = TMP_REG3;
- argw = 0;
- }
- }
-
- if (argw) {
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- compiler->cache_arg = SLJIT_MEM;
- compiler->cache_argw = argw;
- other_r = TMP_REG3;
- argw = 0;
- }
- }
-
- /* No caching here. */
- arg &= REG_MASK;
- argw &= 0x3;
- if (!argw || argw == shift) {
- FAIL_IF(push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(other_r) | (argw ? (1 << 12) : 0)));
- return push_inst(compiler, ADD | RD(arg) | RN(arg) | RM(other_r) | (argw << 10));
- }
- if (arg != reg) {
- FAIL_IF(push_inst(compiler, ADD | RD(arg) | RN(arg) | RM(other_r) | (argw << 10)));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg));
+ if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) {
+ if ((argw >> shift) <= 0xfff) {
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | (argw << (10 - shift)));
}
- FAIL_IF(push_inst(compiler, ADD | RD(TMP_LR) | RN(arg) | RM(other_r) | (argw << 10)));
- FAIL_IF(push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(TMP_LR)));
- return push_inst(compiler, ORR | RD(arg) | RN(TMP_ZERO) | RM(TMP_LR));
- }
-
- if (arg & OFFS_REG_MASK) {
- other_r = OFFS_REG(arg);
- arg &= REG_MASK;
- FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RN(arg) | RM(other_r) | ((argw & 0x3) << 10)));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(tmp_r));
- }
-
- if (compiler->cache_arg == arg) {
- diff = argw - compiler->cache_argw;
- if (diff <= 255 && diff >= -256)
- return push_inst(compiler, sljit_mem_simm[flags & 0x3] | (shift << 30)
- | RT(reg) | RN(TMP_REG3) | ((diff & 0x1ff) << 12));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, diff) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg));
- }
- }
-
- if (argw >= 0 && argw <= 0xffffff && (argw & ((1 << shift) - 1)) == 0) {
- FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_r) | RN(arg & REG_MASK) | ((argw >> 12) << 10)));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30)
- | RT(reg) | RN(tmp_r) | ((argw & 0xfff) << (10 - shift)));
- }
- diff = argw - next_argw;
- next_arg = (arg & REG_MASK) && (arg == next_arg) && diff <= 0xfff && diff >= -0xfff && diff != 0;
- arg &= REG_MASK;
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | ((argw >> 12) << 10)));
- if (arg && compiler->cache_arg == SLJIT_MEM) {
- if (compiler->cache_argw == argw)
- return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3));
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | (argw << 10));
}
}
- compiler->cache_argw = argw;
- if (next_arg && emit_set_delta(compiler, TMP_REG3, arg, argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_arg = SLJIT_MEM | arg;
- arg = 0;
- }
- else {
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- compiler->cache_arg = SLJIT_MEM;
+ if (argw <= 255 && argw >= -256)
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(arg) | ((argw & 0x1ff) << 12));
- if (next_arg) {
- FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RN(TMP_REG3) | RM(arg)));
- compiler->cache_arg = SLJIT_MEM | arg;
- arg = 0;
- }
- }
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
- if (arg)
- return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3));
- return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(TMP_REG3));
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
-{
- if (getput_arg_fast(compiler, flags, reg, arg, argw))
- return compiler->error;
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- return getput_arg(compiler, flags, reg, arg, argw, 0, 0);
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
-{
- if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
- return compiler->error;
- return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+ return push_inst(compiler, STRB | type | RT(reg) | RN(arg) | RM(tmp_reg));
}
/* --------------------------------------------------------------------- */
@@ -1071,14 +864,14 @@ static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, slji
/* --------------------------------------------------------------------- */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 i, tmp, offs, prev, saved_regs_size;
+ sljit_s32 args, i, tmp, offs, prev, saved_regs_size;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 0);
local_size += saved_regs_size + SLJIT_LOCALS_OFFSET;
@@ -1148,6 +941,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(TMP_SP) | (0 << 10)));
}
+ args = get_arg_count(arg_types);
+
if (args >= 1)
FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0) | RN(TMP_ZERO) | RM(SLJIT_R0)));
if (args >= 2)
@@ -1159,12 +954,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 0) + SLJIT_LOCALS_OFFSET;
local_size = (local_size + 15) & ~0xf;
@@ -1297,112 +1092,87 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) {
+ SLJIT_ASSERT(reg_map[1] == 0 && reg_map[3] == 2 && reg_map[5] == 4);
+
+ if (op >= SLJIT_MOV_U8 && op <= SLJIT_MOV_S8)
+ dst = 5;
+ else if (op >= SLJIT_MOV_U16 && op <= SLJIT_MOV_S16)
+ dst = 3;
+ else
+ dst = 1;
+
+ /* Signed word sized load is the prefetch instruction. */
+ return emit_op_mem(compiler, WORD_SIZE | SIGNED, dst, src, srcw, TMP_REG1);
+ }
+ return SLJIT_SUCCESS;
+ }
dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
op = GET_OPCODE(op);
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) {
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+ /* Both operands are registers. */
+ if (dst_r != TMP_REG1 && FAST_IS_REG(src))
+ return emit_op_imm(compiler, op | ((op_flags & SLJIT_I32_OP) ? INT_OP : 0), dst_r, TMP_REG1, src);
+
switch (op) {
case SLJIT_MOV:
case SLJIT_MOV_P:
- flags = WORD_SIZE;
+ mem_flags = WORD_SIZE;
break;
case SLJIT_MOV_U8:
- flags = BYTE_SIZE;
+ mem_flags = BYTE_SIZE;
if (src & SLJIT_IMM)
srcw = (sljit_u8)srcw;
break;
case SLJIT_MOV_S8:
- flags = BYTE_SIZE | SIGNED;
+ mem_flags = BYTE_SIZE | SIGNED;
if (src & SLJIT_IMM)
srcw = (sljit_s8)srcw;
break;
case SLJIT_MOV_U16:
- flags = HALF_SIZE;
+ mem_flags = HALF_SIZE;
if (src & SLJIT_IMM)
srcw = (sljit_u16)srcw;
break;
case SLJIT_MOV_S16:
- flags = HALF_SIZE | SIGNED;
+ mem_flags = HALF_SIZE | SIGNED;
if (src & SLJIT_IMM)
srcw = (sljit_s16)srcw;
break;
case SLJIT_MOV_U32:
- flags = INT_SIZE;
+ mem_flags = INT_SIZE;
if (src & SLJIT_IMM)
srcw = (sljit_u32)srcw;
break;
case SLJIT_MOV_S32:
- flags = INT_SIZE | SIGNED;
- if (src & SLJIT_IMM)
- srcw = (sljit_s32)srcw;
- break;
- case SLJIT_MOVU:
- case SLJIT_MOVU_P:
- flags = WORD_SIZE | UPDATE;
- break;
- case SLJIT_MOVU_U8:
- flags = BYTE_SIZE | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_u8)srcw;
- break;
- case SLJIT_MOVU_S8:
- flags = BYTE_SIZE | SIGNED | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_s8)srcw;
- break;
- case SLJIT_MOVU_U16:
- flags = HALF_SIZE | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_u16)srcw;
- break;
- case SLJIT_MOVU_S16:
- flags = HALF_SIZE | SIGNED | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_s16)srcw;
- break;
- case SLJIT_MOVU_U32:
- flags = INT_SIZE | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_u32)srcw;
- break;
- case SLJIT_MOVU_S32:
- flags = INT_SIZE | SIGNED | UPDATE;
+ mem_flags = INT_SIZE | SIGNED;
if (src & SLJIT_IMM)
srcw = (sljit_s32)srcw;
break;
default:
- SLJIT_ASSERT_STOP();
- flags = 0;
+ SLJIT_UNREACHABLE();
+ mem_flags = 0;
break;
}
if (src & SLJIT_IMM)
FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw));
- else if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, flags, dst_r, src, srcw))
- FAIL_IF(compiler->error);
- else
- FAIL_IF(getput_arg(compiler, flags, dst_r, src, srcw, dst, dstw));
- } else {
- if (dst_r != TMP_REG1)
- return emit_op_imm(compiler, op | ((op_flags & SLJIT_I32_OP) ? INT_OP : 0), dst_r, TMP_REG1, src);
+ else if (!(src & SLJIT_MEM))
dst_r = src;
- }
+ else
+ FAIL_IF(emit_op_mem(compiler, mem_flags, dst_r, src, srcw, TMP_REG1));
- if (dst & SLJIT_MEM) {
- if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw))
- return compiler->error;
- else
- return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0);
- }
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
return SLJIT_SUCCESS;
}
- flags = GET_FLAGS(op_flags) ? SET_FLAGS : 0;
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
mem_flags = WORD_SIZE;
+
if (op_flags & SLJIT_I32_OP) {
flags |= INT_OP;
mem_flags = INT_SIZE;
@@ -1412,28 +1182,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
flags |= UNUSED_RETURN;
if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, mem_flags, TMP_REG2, src, srcw))
- FAIL_IF(compiler->error);
- else
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src, srcw, TMP_REG2));
src = TMP_REG2;
}
- if (src & SLJIT_IMM) {
- flags |= ARG2_IMM;
- if (op_flags & SLJIT_I32_OP)
- srcw = (sljit_s32)srcw;
- } else
- srcw = src;
-
- emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, srcw);
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, src);
- if (dst & SLJIT_MEM) {
- if (getput_arg_fast(compiler, mem_flags | STORE, dst_r, dst, dstw))
- return compiler->error;
- else
- return getput_arg(compiler, mem_flags | STORE, dst_r, dst, dstw, 0, 0);
- }
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
return SLJIT_SUCCESS;
}
@@ -1450,12 +1206,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
- flags = GET_FLAGS(op) ? SET_FLAGS : 0;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
mem_flags = WORD_SIZE;
+
if (op & SLJIT_I32_OP) {
flags |= INT_OP;
mem_flags = INT_SIZE;
@@ -1464,46 +1221,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
if (dst == SLJIT_UNUSED)
flags |= UNUSED_RETURN;
- if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, mem_flags | STORE | ARG_TEST, TMP_REG1, dst, dstw))
- flags |= SLOW_DEST;
-
if (src1 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, mem_flags, TMP_REG1, src1, src1w))
- FAIL_IF(compiler->error);
- else
- flags |= SLOW_SRC1;
- }
- if (src2 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, mem_flags, TMP_REG2, src2, src2w))
- FAIL_IF(compiler->error);
- else
- flags |= SLOW_SRC2;
- }
-
- if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
- if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, dst, dstw));
- }
- else {
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, dst, dstw));
- }
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
}
- else if (flags & SLOW_SRC1)
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, dst, dstw));
- else if (flags & SLOW_SRC2)
- FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, dst, dstw));
- if (src1 & SLJIT_MEM)
- src1 = TMP_REG1;
- if (src2 & SLJIT_MEM)
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src2, src2w, TMP_REG2));
src2 = TMP_REG2;
+ }
if (src1 & SLJIT_IMM)
flags |= ARG1_IMM;
else
src1w = src1;
+
if (src2 & SLJIT_IMM)
flags |= ARG2_IMM;
else
@@ -1511,14 +1243,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w);
- if (dst & SLJIT_MEM) {
- if (!(flags & SLOW_DEST)) {
- getput_arg_fast(compiler, mem_flags | STORE, dst_r, dst, dstw);
- return compiler->error;
- }
- return getput_arg(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, 0, 0);
- }
-
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
return SLJIT_SUCCESS;
}
@@ -1531,7 +1257,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg;
+ return freg_map[reg];
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -1547,74 +1273,60 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#else
- /* Available by default. */
- return 1;
-#endif
-}
-
static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
{
sljit_u32 shift = MEM_SIZE_SHIFT(flags);
- sljit_ins ins_bits = (shift << 30);
- sljit_s32 other_r;
- sljit_sw diff;
+ sljit_ins type = (shift << 30);
SLJIT_ASSERT(arg & SLJIT_MEM);
if (!(flags & STORE))
- ins_bits |= 1 << 22;
+ type |= 0x00400000;
if (arg & OFFS_REG_MASK) {
argw &= 3;
- if (!argw || argw == shift)
- return push_inst(compiler, STR_FR | ins_bits | VT(reg)
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STR_FR | type | VT(reg)
| RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
- other_r = OFFS_REG(arg);
- arg &= REG_MASK;
- FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg) | RM(other_r) | (argw << 10)));
- arg = TMP_REG1;
- argw = 0;
+
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw << 10)));
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1));
}
arg &= REG_MASK;
- if (arg && argw >= 0 && ((argw >> shift) <= 0xfff) && (argw & ((1 << shift) - 1)) == 0)
- return push_inst(compiler, STR_FI | ins_bits | VT(reg) | RN(arg) | (argw << (10 - shift)));
-
- if (arg && argw <= 255 && argw >= -256)
- return push_inst(compiler, STUR_FI | ins_bits | VT(reg) | RN(arg) | ((argw & 0x1ff) << 12));
-
- /* Slow cases */
- if (compiler->cache_arg == SLJIT_MEM && argw != compiler->cache_argw) {
- diff = argw - compiler->cache_argw;
- if (!arg && diff <= 255 && diff >= -256)
- return push_inst(compiler, STUR_FI | ins_bits | VT(reg) | RN(TMP_REG3) | ((diff & 0x1ff) << 12));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- }
+
+ if (arg == SLJIT_UNUSED) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw & ~(0xfff << shift)));
+
+ argw = (argw >> shift) & 0xfff;
+
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | (argw << 10));
}
- if (compiler->cache_arg != SLJIT_MEM || argw != compiler->cache_argw) {
- compiler->cache_arg = SLJIT_MEM;
- compiler->cache_argw = argw;
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
+ if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) {
+ if ((argw >> shift) <= 0xfff)
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(arg) | (argw << (10 - shift)));
+
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(TMP_REG1) | RN(arg) | ((argw >> 12) << 10)));
+
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | (argw << 10));
+ }
}
- if (arg & REG_MASK)
- return push_inst(compiler, STR_FR | ins_bits | VT(reg) | RN(arg) | RM(TMP_REG3));
- return push_inst(compiler, STR_FI | ins_bits | VT(reg) | RN(TMP_REG3));
+ if (argw <= 255 && argw >= -256)
+ return push_inst(compiler, STUR_FI | type | VT(reg) | RN(arg) | ((argw & 0x1ff) << 12));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
+ return push_inst(compiler, STR_FR | type | VT(reg) | RN(arg) | RM(TMP_REG1));
}
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
sljit_ins inv_bits = (op & SLJIT_F32_OP) ? (1 << 22) : 0;
if (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64)
@@ -1627,8 +1339,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
FAIL_IF(push_inst(compiler, (FCVTZS ^ inv_bits) | RD(dst_r) | VN(src)));
- if (dst_r == TMP_REG1 && dst != SLJIT_UNUSED)
- return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw);
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw, TMP_REG2);
return SLJIT_SUCCESS;
}
@@ -1643,7 +1355,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
inv_bits |= (1 << 31);
if (src & SLJIT_MEM) {
- emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw);
+ emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw, TMP_REG1);
src = TMP_REG1;
} else if (src & SLJIT_IMM) {
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
@@ -1689,17 +1401,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
sljit_ins inv_bits;
CHECK_ERROR();
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x100) == WORD_SIZE, must_be_one_bit_difference);
+ SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x1) == WORD_SIZE, must_be_one_bit_difference);
SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
inv_bits = (op & SLJIT_F32_OP) ? (1 << 22) : 0;
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src & SLJIT_MEM) {
- emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x100) : mem_flags, dst_r, src, srcw);
+ emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x1) : mem_flags, dst_r, src, srcw);
src = dst_r;
}
@@ -1742,9 +1452,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
-
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src1 & SLJIT_MEM) {
emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w);
@@ -1785,15 +1492,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(TMP_LR));
/* Memory. */
- return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw);
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw, TMP_REG1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
@@ -1804,10 +1507,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, ORR | RD(TMP_LR) | RN(TMP_ZERO) | RM(src)));
- else if (src & SLJIT_MEM)
- FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw));
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, TMP_LR, srcw));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw, TMP_REG1));
return push_inst(compiler, RET | RN(TMP_LR));
}
@@ -1866,7 +1567,7 @@ static sljit_uw get_cc(sljit_s32 type)
return 0x6;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return 0xe;
}
}
@@ -1913,6 +1614,20 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
return jump;
}
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+}
+
static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compiler, sljit_s32 type,
sljit_s32 src, sljit_sw srcw)
{
@@ -1928,13 +1643,14 @@ static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compi
jump->flags |= IS_CBZ | IS_COND;
if (src & SLJIT_MEM) {
- PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw));
+ PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
src = TMP_REG1;
}
else if (src & SLJIT_IMM) {
PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
src = TMP_REG1;
}
+
SLJIT_ASSERT(FAST_IS_REG(src));
if ((type & 0xff) == SLJIT_EQUAL)
@@ -1955,15 +1671,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
- /* In ARM, we don't need to touch the arguments. */
if (!(src & SLJIT_IMM)) {
if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
src = TMP_REG1;
}
return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(src));
}
+ /* These jumps are converted to jump/call instructions when possible. */
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF(!jump);
set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
@@ -1974,54 +1690,170 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1));
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
- sljit_s32 dst_r, flags, mem_flags;
+ sljit_s32 dst_r, src_r, flags, mem_flags;
sljit_ins cc;
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- ADJUST_LOCAL_OFFSET(src, srcw);
-
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
cc = get_cc(type & 0xff);
dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
if (GET_OPCODE(op) < SLJIT_ADD) {
FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(dst_r) | RN(TMP_ZERO) | RM(TMP_ZERO)));
- if (dst_r != TMP_REG1)
- return SLJIT_SUCCESS;
- return emit_op_mem(compiler, (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE, TMP_REG1, dst, dstw);
+
+ if (dst_r == TMP_REG1) {
+ mem_flags = (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE;
+ return emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG2);
+ }
+
+ return SLJIT_SUCCESS;
}
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- flags = GET_FLAGS(op) ? SET_FLAGS : 0;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
mem_flags = WORD_SIZE;
+
if (op & SLJIT_I32_OP) {
flags |= INT_OP;
mem_flags = INT_SIZE;
}
- if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, mem_flags, TMP_REG1, src, srcw, dst, dstw));
+ src_r = dst;
+
+ if (dst & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG1));
+ src_r = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(TMP_ZERO)));
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src_r, TMP_REG2);
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins inv_bits = (dst_reg & SLJIT_I32_OP) ? (1 << 31) : 0;
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ if (dst_reg & SLJIT_I32_OP)
+ srcw = (sljit_s32)srcw;
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
src = TMP_REG1;
srcw = 0;
- } else if (src & SLJIT_IMM)
- flags |= ARG1_IMM;
+ }
- FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(TMP_ZERO)));
- emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src, TMP_REG2);
+ cc = get_cc(type & 0xff);
+ dst_reg &= ~SLJIT_I32_OP;
+
+ return push_inst(compiler, (CSEL ^ inv_bits) | (cc << 12) | RD(dst_reg) | RN(dst_reg) | RM(src));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 sign = 0, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
- if (dst_r != TMP_REG1)
+ if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
return SLJIT_SUCCESS;
- return emit_op_mem2(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, 0, 0);
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S8:
+ sign = 1;
+ case SLJIT_MOV_U8:
+ inst = STURBI | (MEM_SIZE_SHIFT(BYTE_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S16:
+ sign = 1;
+ case SLJIT_MOV_U16:
+ inst = STURBI | (MEM_SIZE_SHIFT(HALF_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S32:
+ sign = 1;
+ case SLJIT_MOV_U32:
+ inst = STURBI | (MEM_SIZE_SHIFT(INT_SIZE) << 30) | 0x400;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= sign ? 0x00800000 : 0x00400000;
+
+ if (type & SLJIT_MEM_PRE)
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | RT(reg) | RN(mem & REG_MASK) | ((memw & 0x1ff) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = STUR_FI | 0x80000400;
+
+ if (!(type & SLJIT_F32_OP))
+ inst |= 0x40000000;
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= 0x00400000;
+
+ if (type & SLJIT_MEM_PRE)
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | VT(freg) | RN(mem & REG_MASK) | ((memw & 0x1ff) << 12));
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
@@ -2037,11 +1869,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
PTR_FAIL_IF(!const_);
set_const(const_, compiler);
- dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, init_value));
if (dst & SLJIT_MEM)
- PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw));
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
return const_;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c
index 95afc5231f..75e7a38b5f 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -26,7 +26,11 @@
SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
{
- return "ARM-Thumb2" SLJIT_CPUINFO;
+#ifdef __SOFTFP__
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:softfp";
+#else
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:hardfp";
+#endif
}
/* Length of an instruction word. */
@@ -35,15 +39,18 @@ typedef sljit_u32 sljit_ins;
/* Last register + 1. */
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
-#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 5)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_FREG1 (0)
-#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
-static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
- 0, 0, 1, 2, 12, 11, 10, 9, 8, 7, 6, 5, 13, 3, 4, 14, 15
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7
};
#define COPY_BITS(src, from, to, bits) \
@@ -70,9 +77,9 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define RN4(rn) (reg_map[rn] << 16)
#define RM4(rm) (reg_map[rm])
#define RT4(rt) (reg_map[rt] << 12)
-#define DD4(dd) ((dd) << 12)
-#define DN4(dn) ((dn) << 16)
-#define DM4(dm) (dm)
+#define DD4(dd) (freg_map[dd] << 12)
+#define DN4(dn) (freg_map[dn] << 16)
+#define DM4(dm) (freg_map[dm])
#define IMM5(imm) \
(COPY_BITS(imm, 2, 12, 3) | ((imm & 0x3) << 6))
#define IMM12(imm) \
@@ -108,7 +115,11 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define BLX 0x4780
#define BX 0x4700
#define CLZ 0xfab0f080
+#define CMNI_W 0xf1100f00
+#define CMP 0x4280
#define CMPI 0x2800
+#define CMPI_W 0xf1b00f00
+#define CMP_X 0x4500
#define CMP_W 0xebb00f00
#define EORI 0xf0800000
#define EORS 0x4040
@@ -175,6 +186,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define VDIV_F32 0xee800a00
#define VMOV_F32 0xeeb00a40
#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
#define VMRS 0xeef1fa10
#define VMUL_F32 0xee200a00
#define VNEG_F32 0xeeb10a40
@@ -205,10 +217,10 @@ static sljit_s32 push_inst32(struct sljit_compiler *compiler, sljit_ins inst)
static SLJIT_INLINE sljit_s32 emit_imm32_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
{
- FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) |
- COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
- return push_inst32(compiler, MOVT | RD4(dst) |
- COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
}
static SLJIT_INLINE void modify_imm32_const(sljit_u16 *inst, sljit_uw new_imm)
@@ -338,7 +350,7 @@ static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump, sljit_sw
else if (type == 6) /* Encoding T1 of 'BL' instruction */
jump_inst[1] |= 0xd000;
else
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
@@ -430,6 +442,26 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return (void*)((sljit_uw)code | 0x1);
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Core code generator functions. */
/* --------------------------------------------------------------------- */
@@ -498,36 +530,32 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst,
}
/* set low 16 bits, set hi 16 bits to 0. */
- FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) |
- COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
/* set hi 16 bit if needed. */
if (imm >= 0x10000)
- return push_inst32(compiler, MOVT | RD4(dst) |
- COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
return SLJIT_SUCCESS;
}
#define ARG1_IMM 0x0010000
#define ARG2_IMM 0x0020000
-#define KEEP_FLAGS 0x0040000
/* SET_FLAGS must be 0x100000 as it is also the value of S bit (can be used for optimization). */
#define SET_FLAGS 0x0100000
#define UNUSED_RETURN 0x0200000
-#define SLOW_DEST 0x0400000
-#define SLOW_SRC1 0x0800000
-#define SLOW_SRC2 0x1000000
static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_uw arg1, sljit_uw arg2)
{
/* dst must be register, TMP_REG1
- arg1 must be register, TMP_REG1, imm
- arg2 must be register, TMP_REG2, imm */
+ arg1 must be register, imm
+ arg2 must be register, imm */
sljit_s32 reg;
sljit_uw imm, nimm;
if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) {
- /* Both are immediates. */
+ /* Both are immediates, no temporaries are used. */
flags &= ~ARG1_IMM;
FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
arg1 = TMP_REG1;
@@ -543,7 +571,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
/* No form with immediate operand. */
break;
case SLJIT_MOV:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG2);
return load_immediate(compiler, dst, imm);
case SLJIT_NOT:
if (!(flags & SET_FLAGS))
@@ -553,7 +581,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
break;
case SLJIT_ADD:
nimm = -imm;
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) {
+ if (IS_2_LO_REGS(reg, dst)) {
if (imm <= 0x7)
return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
if (nimm <= 0x7)
@@ -571,9 +599,12 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
if (nimm <= 0xfff)
return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(nimm));
}
- imm = get_imm(imm);
- if (imm != INVALID_IMM)
- return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ nimm = get_imm(imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm);
+ nimm = get_imm(-imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm);
break;
case SLJIT_ADDC:
imm = get_imm(imm);
@@ -581,16 +612,27 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
return push_inst32(compiler, ADCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
break;
case SLJIT_SUB:
+ /* SUB operation can be replaced by ADD because of the negative carry flag. */
if (flags & ARG1_IMM) {
- if (!(flags & KEEP_FLAGS) && imm == 0 && IS_2_LO_REGS(reg, dst))
+ if (imm == 0 && IS_2_LO_REGS(reg, dst))
return push_inst16(compiler, RSBSI | RD3(dst) | RN3(reg));
imm = get_imm(imm);
if (imm != INVALID_IMM)
return push_inst32(compiler, RSB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
break;
}
+ if (flags & UNUSED_RETURN) {
+ if (imm <= 0xff && reg_map[reg] <= 7)
+ return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg));
+ nimm = get_imm(imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, CMPI_W | RN4(reg) | nimm);
+ nimm = get_imm(-imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, CMNI_W | RN4(reg) | nimm);
+ }
nimm = -imm;
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) {
+ if (IS_2_LO_REGS(reg, dst)) {
if (imm <= 0x7)
return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
if (nimm <= 0x7)
@@ -601,8 +643,6 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
if (nimm <= 0xff)
return push_inst16(compiler, ADDSI8 | IMM8(nimm) | RDN3(dst));
}
- if (imm <= 0xff && (flags & UNUSED_RETURN))
- return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg));
}
if (!(flags & SET_FLAGS)) {
if (imm <= 0xfff)
@@ -610,9 +650,12 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
if (nimm <= 0xfff)
return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(nimm));
}
- imm = get_imm(imm);
- if (imm != INVALID_IMM)
- return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ nimm = get_imm(imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm);
+ nimm = get_imm(-imm);
+ if (nimm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm);
break;
case SLJIT_SUBC:
if (flags & ARG1_IMM)
@@ -657,31 +700,35 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
}
switch (flags & 0xffff) {
case SLJIT_SHL:
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg))
+ if (IS_2_LO_REGS(dst, reg))
return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6));
return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
case SLJIT_LSHR:
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg))
+ if (IS_2_LO_REGS(dst, reg))
return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6));
return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
default: /* SLJIT_ASHR */
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg))
+ if (IS_2_LO_REGS(dst, reg))
return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6));
return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
}
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
if (flags & ARG2_IMM) {
- FAIL_IF(load_immediate(compiler, TMP_REG2, arg2));
- arg2 = TMP_REG2;
+ imm = arg2;
+ arg2 = (arg1 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, arg2, imm));
}
else {
- FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
- arg1 = TMP_REG1;
+ imm = arg1;
+ arg1 = (arg2 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, arg1, imm));
}
+
+ SLJIT_ASSERT(arg1 != arg2);
}
/* Both arguments are registers. */
@@ -690,108 +737,98 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
case SLJIT_MOV_U32:
case SLJIT_MOV_S32:
case SLJIT_MOV_P:
- case SLJIT_MOVU:
- case SLJIT_MOVU_U32:
- case SLJIT_MOVU_S32:
- case SLJIT_MOVU_P:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
if (dst == arg2)
return SLJIT_SUCCESS;
return push_inst16(compiler, MOV | SET_REGS44(dst, arg2));
case SLJIT_MOV_U8:
- case SLJIT_MOVU_U8:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
if (IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, UXTB | RD3(dst) | RN3(arg2));
return push_inst32(compiler, UXTB_W | RD4(dst) | RM4(arg2));
case SLJIT_MOV_S8:
- case SLJIT_MOVU_S8:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
if (IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, SXTB | RD3(dst) | RN3(arg2));
return push_inst32(compiler, SXTB_W | RD4(dst) | RM4(arg2));
case SLJIT_MOV_U16:
- case SLJIT_MOVU_U16:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
if (IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, UXTH | RD3(dst) | RN3(arg2));
return push_inst32(compiler, UXTH_W | RD4(dst) | RM4(arg2));
case SLJIT_MOV_S16:
- case SLJIT_MOVU_S16:
- SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
if (IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, SXTH | RD3(dst) | RN3(arg2));
return push_inst32(compiler, SXTH_W | RD4(dst) | RM4(arg2));
case SLJIT_NOT:
- SLJIT_ASSERT(arg1 == TMP_REG1);
- if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, MVNS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2));
case SLJIT_CLZ:
- SLJIT_ASSERT(arg1 == TMP_REG1);
+ SLJIT_ASSERT(arg1 == TMP_REG2);
FAIL_IF(push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2)));
- if (flags & SET_FLAGS) {
- if (reg_map[dst] <= 7)
- return push_inst16(compiler, CMPI | RDN3(dst));
- return push_inst32(compiler, ADD_WI | SET_FLAGS | RN4(dst) | RD4(dst));
- }
return SLJIT_SUCCESS;
case SLJIT_ADD:
- if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2))
+ if (IS_3_LO_REGS(dst, arg1, arg2))
return push_inst16(compiler, ADDS | RD3(dst) | RN3(arg1) | RM3(arg2));
if (dst == arg1 && !(flags & SET_FLAGS))
return push_inst16(compiler, ADD | SET_REGS44(dst, arg2));
return push_inst32(compiler, ADD_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_ADDC:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, ADCS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, ADC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_SUB:
- if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2))
+ if (flags & UNUSED_RETURN) {
+ if (IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, CMP | RD3(arg1) | RN3(arg2));
+ return push_inst16(compiler, CMP_X | SET_REGS44(arg1, arg2));
+ }
+ if (IS_3_LO_REGS(dst, arg1, arg2))
return push_inst16(compiler, SUBS | RD3(dst) | RN3(arg1) | RM3(arg2));
return push_inst32(compiler, SUB_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_SUBC:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, SBCS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, SBC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_MUL:
if (!(flags & SET_FLAGS))
return push_inst32(compiler, MUL | RD4(dst) | RN4(arg1) | RM4(arg2));
- SLJIT_ASSERT(reg_map[TMP_REG2] <= 7 && dst != TMP_REG2);
+ SLJIT_ASSERT(dst != TMP_REG2);
FAIL_IF(push_inst32(compiler, SMULL | RT4(dst) | RD4(TMP_REG2) | RN4(arg1) | RM4(arg2)));
/* cmp TMP_REG2, dst asr #31. */
return push_inst32(compiler, CMP_W | RN4(TMP_REG2) | 0x70e0 | RM4(dst));
case SLJIT_AND:
- if (!(flags & KEEP_FLAGS)) {
- if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
- return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2));
- if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2))
- return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2));
- }
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2));
+ if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2));
return push_inst32(compiler, AND_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_OR:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, ORRS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, ORR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_XOR:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_SHL:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_LSHR:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
case SLJIT_ASHR:
- if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2))
+ if (dst == arg1 && IS_2_LO_REGS(dst, arg2))
return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2));
return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
@@ -801,9 +838,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s
#define WORD_SIZE 0x00
#define BYTE_SIZE 0x04
#define HALF_SIZE 0x08
-
-#define UPDATE 0x10
-#define ARG_TEST 0x20
+#define PRELOAD 0x0c
#define IS_WORD_SIZE(flags) (!(flags & (BYTE_SIZE | HALF_SIZE)))
#define OFFSET_CHECK(imm, shift) (!(argw & ~(imm << shift)))
@@ -859,7 +894,7 @@ static const sljit_ins sljit_mem16_imm5[12] = {
#define MEM_IMM8 0xc00
#define MEM_IMM12 0x800000
-static const sljit_ins sljit_mem32[12] = {
+static const sljit_ins sljit_mem32[13] = {
/* w u l */ 0xf8500000 /* ldr.w */,
/* w u s */ 0xf8400000 /* str.w */,
/* w s l */ 0xf8500000 /* ldr.w */,
@@ -874,6 +909,8 @@ static const sljit_ins sljit_mem32[12] = {
/* h u s */ 0xf8200000 /* strsh.w */,
/* h s l */ 0xf9300000 /* ldrsh.w */,
/* h s s */ 0xf8200000 /* strsh.w */,
+
+/* p u l */ 0xf8100000 /* pld */,
};
/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
@@ -897,240 +934,92 @@ static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst,
return SLJIT_ERR_UNSUPPORTED;
}
-/* Can perform an operation using at most 1 instruction. */
-static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
{
- sljit_s32 other_r, shift;
+ sljit_s32 other_r;
+ sljit_uw tmp;
SLJIT_ASSERT(arg & SLJIT_MEM);
-
- if (SLJIT_UNLIKELY(flags & UPDATE)) {
- if ((arg & REG_MASK) && !(arg & OFFS_REG_MASK) && argw <= 0xff && argw >= -0xff) {
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
-
- flags &= ~UPDATE;
- arg &= 0xf;
- if (argw >= 0)
- argw |= 0x200;
- else {
- argw = -argw;
- }
-
- SLJIT_ASSERT(argw >= 0 && (argw & 0xff) <= 0xff);
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | 0x100 | argw));
- return -1;
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg);
+ arg &= ~SLJIT_MEM;
+
+ if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
+ tmp = get_imm(argw & ~0xfff);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | tmp));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg) | (argw & 0xfff));
}
- return 0;
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+ if (IS_2_LO_REGS(reg, tmp_reg) && sljit_mem16_imm5[flags])
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg));
}
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
-
argw &= 0x3;
other_r = OFFS_REG(arg);
arg &= 0xf;
if (!argw && IS_3_LO_REGS(reg, arg, other_r))
- FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r)));
- else
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4)));
- return -1;
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4));
}
- if (!(arg & REG_MASK) || argw > 0xfff || argw < -0xff)
- return 0;
-
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
+ if (argw > 0xfff) {
+ tmp = get_imm(argw & ~0xfff);
+ if (tmp != INVALID_IMM) {
+ push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | tmp);
+ arg = tmp_reg;
+ argw = argw & 0xfff;
+ }
+ }
+ else if (argw < -0xff) {
+ tmp = get_imm(-argw & ~0xff);
+ if (tmp != INVALID_IMM) {
+ push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | tmp);
+ arg = tmp_reg;
+ argw = -(-argw & 0xff);
+ }
+ }
- arg &= 0xf;
if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) {
- shift = 3;
+ tmp = 3;
if (IS_WORD_SIZE(flags)) {
if (OFFSET_CHECK(0x1f, 2))
- shift = 2;
+ tmp = 2;
}
else if (flags & BYTE_SIZE)
{
if (OFFSET_CHECK(0x1f, 0))
- shift = 0;
+ tmp = 0;
}
else {
SLJIT_ASSERT(flags & HALF_SIZE);
if (OFFSET_CHECK(0x1f, 1))
- shift = 1;
+ tmp = 1;
}
- if (shift != 3) {
- FAIL_IF(push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | (argw << (6 - shift))));
- return -1;
- }
- }
-
- /* SP based immediate. */
- if (SLJIT_UNLIKELY(arg == SLJIT_SP) && OFFSET_CHECK(0xff, 2) && IS_WORD_SIZE(flags) && reg_map[reg] <= 7) {
- FAIL_IF(push_inst16(compiler, STR_SP | ((flags & STORE) ? 0 : 0x800) | RDN3(reg) | (argw >> 2)));
- return -1;
+ if (tmp < 3)
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | (argw << (6 - tmp)));
}
-
- if (argw >= 0)
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw));
- else
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | -argw));
- return -1;
-}
-
-/* see getput_arg below.
- Note: can_cache is called only for binary operators. Those
- operators always uses word arguments without write back. */
-static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_sw diff;
- if ((arg & OFFS_REG_MASK) || !(next_arg & SLJIT_MEM))
- return 0;
-
- if (!(arg & REG_MASK)) {
- diff = argw - next_argw;
- if (diff <= 0xfff && diff >= -0xfff)
- return 1;
- return 0;
+ else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && OFFSET_CHECK(0xff, 2) && reg_map[reg] <= 7) {
+ /* SP based immediate. */
+ return push_inst16(compiler, STR_SP | ((flags & STORE) ? 0 : 0x800) | RDN3(reg) | (argw >> 2));
}
- if (argw == next_argw)
- return 1;
+ if (argw >= 0 && argw <= 0xfff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw);
+ else if (argw < 0 && argw >= -0xff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | -argw);
- diff = argw - next_argw;
- if (arg == next_arg && diff <= 0xfff && diff >= -0xfff)
- return 1;
+ SLJIT_ASSERT(arg != tmp_reg);
- return 0;
-}
-
-/* Emit the necessary instructions. See can_cache above. */
-static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
- sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_s32 tmp_r, other_r;
- sljit_sw diff;
-
- SLJIT_ASSERT(arg & SLJIT_MEM);
- if (!(next_arg & SLJIT_MEM)) {
- next_arg = 0;
- next_argw = 0;
- }
-
- tmp_r = (flags & STORE) ? TMP_REG3 : reg;
-
- if (SLJIT_UNLIKELY((flags & UPDATE) && (arg & REG_MASK))) {
- /* Update only applies if a base register exists. */
- /* There is no caching here. */
- other_r = OFFS_REG(arg);
- arg &= 0xf;
- flags &= ~UPDATE;
-
- if (!other_r) {
- if (!(argw & ~0xfff)) {
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw));
- return push_inst32(compiler, ADDWI | RD4(arg) | RN4(arg) | IMM12(argw));
- }
-
- if (compiler->cache_arg == SLJIT_MEM) {
- if (argw == compiler->cache_argw) {
- other_r = TMP_REG3;
- argw = 0;
- }
- else if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- other_r = TMP_REG3;
- argw = 0;
- }
- }
-
- if (argw) {
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- compiler->cache_arg = SLJIT_MEM;
- compiler->cache_argw = argw;
- other_r = TMP_REG3;
- argw = 0;
- }
- }
-
- argw &= 0x3;
- if (!argw && IS_3_LO_REGS(reg, arg, other_r)) {
- FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r)));
- return push_inst16(compiler, ADD | SET_REGS44(arg, other_r));
- }
- FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4)));
- return push_inst32(compiler, ADD_W | RD4(arg) | RN4(arg) | RM4(other_r) | (argw << 6));
- }
- flags &= ~UPDATE;
-
- SLJIT_ASSERT(!(arg & OFFS_REG_MASK));
-
- if (compiler->cache_arg == arg) {
- diff = argw - compiler->cache_argw;
- if (!(diff & ~0xfff))
- return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | diff);
- if (!((compiler->cache_argw - argw) & ~0xff))
- return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(TMP_REG3) | (compiler->cache_argw - argw));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, diff) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0);
- }
- }
-
- next_arg = (arg & REG_MASK) && (arg == next_arg) && (argw != next_argw);
- arg &= 0xf;
- if (arg && compiler->cache_arg == SLJIT_MEM) {
- if (compiler->cache_argw == argw)
- return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3));
- }
- }
-
- compiler->cache_argw = argw;
- if (next_arg && emit_set_delta(compiler, TMP_REG3, arg, argw) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_arg = SLJIT_MEM | arg;
- arg = 0;
- }
- else {
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- compiler->cache_arg = SLJIT_MEM;
-
- diff = argw - next_argw;
- if (next_arg && diff <= 0xfff && diff >= -0xfff) {
- FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, arg)));
- compiler->cache_arg = SLJIT_MEM | arg;
- arg = 0;
- }
- }
-
- if (arg)
- return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3));
- return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0);
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
-{
- if (getput_arg_fast(compiler, flags, reg, arg, argw))
- return compiler->error;
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- return getput_arg(compiler, flags, reg, arg, argw, 0, 0);
-}
-
-static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
-{
- if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
- return compiler->error;
- return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+ if (IS_3_LO_REGS(reg, arg, tmp_reg))
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp_reg));
}
/* --------------------------------------------------------------------- */
@@ -1138,17 +1027,15 @@ static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, slji
/* --------------------------------------------------------------------- */
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 size, i, tmp;
- sljit_ins push;
+ sljit_s32 args, size, i, tmp;
+ sljit_ins push = 0;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
-
- push = (1 << 4);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG;
for (i = SLJIT_S0; i >= tmp; i--)
@@ -1162,7 +1049,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
: push_inst16(compiler, PUSH | (1 << 8) | push));
/* Stack must be aligned to 8 bytes: (LR, R4) */
- size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2);
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
local_size = ((size + local_size + 7) & ~7) - size;
compiler->local_size = local_size;
if (local_size > 0) {
@@ -1172,6 +1059,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, local_size));
}
+ args = get_arg_count(arg_types);
+
if (args >= 1)
FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_S0, SLJIT_R0)));
if (args >= 2)
@@ -1183,16 +1072,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
sljit_s32 size;
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
- size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2);
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
compiler->local_size = ((size + local_size + 7) & ~7) - size;
return SLJIT_SUCCESS;
}
@@ -1200,7 +1089,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
{
sljit_s32 i, tmp;
- sljit_ins pop;
+ sljit_ins pop = 0;
CHECK_ERROR();
CHECK(check_sljit_emit_return(compiler, op, src, srcw));
@@ -1214,8 +1103,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
FAIL_IF(emit_op_imm(compiler, SLJIT_ADD | ARG2_IMM, SLJIT_SP, SLJIT_SP, compiler->local_size));
}
- pop = (1 << 4);
-
tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG;
for (i = SLJIT_S0; i >= tmp; i--)
pop |= 1 << reg_map[i];
@@ -1273,11 +1160,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
case SLJIT_DIV_UW:
case SLJIT_DIV_SW:
SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
- SLJIT_COMPILE_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 12, bad_register_mapping);
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
saved_reg_count = 0;
if (compiler->scratches >= 4)
- saved_reg_list[saved_reg_count++] = 12;
+ saved_reg_list[saved_reg_count++] = 3;
if (compiler->scratches >= 3)
saved_reg_list[saved_reg_count++] = 2;
if (op >= SLJIT_DIV_UW)
@@ -1333,13 +1220,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+ /* Since TMP_PC has index 15, IS_2_LO_REGS and IS_3_LO_REGS checks always fail. */
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM))
+ return emit_op_mem(compiler, PRELOAD, TMP_PC, src, srcw, TMP_REG1);
+ return SLJIT_SUCCESS;
+ }
dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
op = GET_OPCODE(op);
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) {
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
switch (op) {
case SLJIT_MOV:
case SLJIT_MOV_U32:
@@ -1367,58 +1258,26 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
if (src & SLJIT_IMM)
srcw = (sljit_s16)srcw;
break;
- case SLJIT_MOVU:
- case SLJIT_MOVU_U32:
- case SLJIT_MOVU_S32:
- case SLJIT_MOVU_P:
- flags = WORD_SIZE | UPDATE;
- break;
- case SLJIT_MOVU_U8:
- flags = BYTE_SIZE | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_u8)srcw;
- break;
- case SLJIT_MOVU_S8:
- flags = BYTE_SIZE | SIGNED | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_s8)srcw;
- break;
- case SLJIT_MOVU_U16:
- flags = HALF_SIZE | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_u16)srcw;
- break;
- case SLJIT_MOVU_S16:
- flags = HALF_SIZE | SIGNED | UPDATE;
- if (src & SLJIT_IMM)
- srcw = (sljit_s16)srcw;
- break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
flags = 0;
break;
}
if (src & SLJIT_IMM)
- FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw));
+ FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG2, srcw));
else if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, flags, dst_r, src, srcw))
- FAIL_IF(compiler->error);
- else
- FAIL_IF(getput_arg(compiler, flags, dst_r, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, flags, dst_r, src, srcw, TMP_REG1));
} else {
if (dst_r != TMP_REG1)
- return emit_op_imm(compiler, op, dst_r, TMP_REG1, src);
+ return emit_op_imm(compiler, op, dst_r, TMP_REG2, src);
dst_r = src;
}
- if (dst & SLJIT_MEM) {
- if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw))
- return compiler->error;
- else
- return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0);
- }
- return SLJIT_SUCCESS;
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
}
if (op == SLJIT_NEG) {
@@ -1429,28 +1288,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
return sljit_emit_op2(compiler, SLJIT_SUB | op_flags, dst, dstw, SLJIT_IMM, 0, src, srcw);
}
- flags = (GET_FLAGS(op_flags) ? SET_FLAGS : 0) | ((op_flags & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0);
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
+
if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src, srcw))
- FAIL_IF(compiler->error);
- else
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, dst, dstw));
- src = TMP_REG2;
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
}
- if (src & SLJIT_IMM)
- flags |= ARG2_IMM;
- else
- srcw = src;
-
- emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, srcw);
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG2, src);
- if (dst & SLJIT_MEM) {
- if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw))
- return compiler->error;
- else
- return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0);
- }
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
return SLJIT_SUCCESS;
}
@@ -1459,7 +1307,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- sljit_s32 dst_r, flags;
+ sljit_s32 dst_reg, flags, src2_reg;
CHECK_ERROR();
CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
@@ -1467,70 +1315,39 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
-
- dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
- flags = (GET_FLAGS(op) ? SET_FLAGS : 0) | ((op & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0);
-
- if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, WORD_SIZE | STORE | ARG_TEST, TMP_REG1, dst, dstw))
- flags |= SLOW_DEST;
-
- if (src1 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG1, src1, src1w))
- FAIL_IF(compiler->error);
- else
- flags |= SLOW_SRC1;
- }
- if (src2 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src2, src2w))
- FAIL_IF(compiler->error);
- else
- flags |= SLOW_SRC2;
- }
-
- if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
- if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw));
- }
- else {
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw));
- }
- }
- else if (flags & SLOW_SRC1)
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw));
- else if (flags & SLOW_SRC2)
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw));
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
- if (src1 & SLJIT_MEM)
- src1 = TMP_REG1;
- if (src2 & SLJIT_MEM)
- src2 = TMP_REG2;
+ dst_reg = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
if (src1 & SLJIT_IMM)
flags |= ARG1_IMM;
+ else if (src1 & SLJIT_MEM) {
+ emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1);
+ src1w = TMP_REG1;
+ }
else
src1w = src1;
+
if (src2 & SLJIT_IMM)
flags |= ARG2_IMM;
+ else if (src2 & SLJIT_MEM) {
+ src2_reg = (!(flags & ARG1_IMM) && (src1w == TMP_REG1)) ? TMP_REG2 : TMP_REG1;
+ emit_op_mem(compiler, WORD_SIZE, src2_reg, src2, src2w, src2_reg);
+ src2w = src2_reg;
+ }
else
src2w = src2;
if (dst == SLJIT_UNUSED)
flags |= UNUSED_RETURN;
- emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w);
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_reg, src1w, src2w);
- if (dst & SLJIT_MEM) {
- if (!(flags & SLOW_DEST)) {
- getput_arg_fast(compiler, WORD_SIZE | STORE, dst_r, dst, dstw);
- return compiler->error;
- }
- return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, 0, 0);
- }
- return SLJIT_SUCCESS;
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG2);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
@@ -1542,7 +1359,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg << 1;
+ return (freg_map[reg] << 1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -1560,21 +1377,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#else
- /* Available by default. */
- return 1;
-#endif
-}
-
#define FPU_LOAD (1 << 20)
static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
{
- sljit_sw tmp;
sljit_uw imm;
sljit_sw inst = VSTR_F32 | (flags & (SLJIT_F32_OP | FPU_LOAD));
@@ -1582,8 +1388,8 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags,
/* Fast loads and stores. */
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
- FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG2) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((argw & 0x3) << 6)));
- arg = SLJIT_MEM | TMP_REG2;
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((argw & 0x3) << 6)));
+ arg = SLJIT_MEM | TMP_REG1;
argw = 0;
}
@@ -1594,21 +1400,6 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags,
return push_inst32(compiler, inst | RN4(arg & REG_MASK) | DD4(reg) | (-argw >> 2));
}
- /* Slow cases */
- SLJIT_ASSERT(!(arg & OFFS_REG_MASK));
- if (compiler->cache_arg == arg) {
- tmp = argw - compiler->cache_argw;
- if (!(tmp & ~0x3fc))
- return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg) | (tmp >> 2));
- if (!(-tmp & ~0x3fc))
- return push_inst32(compiler, inst | RN4(TMP_REG3) | DD4(reg) | (-tmp >> 2));
- if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) {
- FAIL_IF(compiler->error);
- compiler->cache_argw = argw;
- return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg));
- }
- }
-
if (arg & REG_MASK) {
if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) {
FAIL_IF(compiler->error);
@@ -1627,19 +1418,18 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags,
}
}
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
-
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
if (arg & REG_MASK)
- FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, (arg & REG_MASK))));
- return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, (arg & REG_MASK))));
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg));
}
static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
+ op ^= SLJIT_F32_OP;
+
if (src & SLJIT_MEM) {
FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src, srcw));
src = TMP_FREG1;
@@ -1647,9 +1437,6 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
FAIL_IF(push_inst32(compiler, VCVT_S32_F32 | (op & SLJIT_F32_OP) | DD4(TMP_FREG1) | DM4(src)));
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst32(compiler, VMOV | (1 << 20) | RT4(dst) | DN4(TMP_FREG1));
@@ -1663,6 +1450,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
{
sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ op ^= SLJIT_F32_OP;
+
if (FAST_IS_REG(src))
FAIL_IF(push_inst32(compiler, VMOV | RT4(src) | DN4(TMP_FREG1)));
else if (src & SLJIT_MEM) {
@@ -1685,6 +1474,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
+ op ^= SLJIT_F32_OP;
+
if (src1 & SLJIT_MEM) {
emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src1, src1w);
src1 = TMP_FREG1;
@@ -1706,16 +1497,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
sljit_s32 dst_r;
CHECK_ERROR();
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
- op ^= SLJIT_F32_OP;
SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100), float_transfer_bit_error);
SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_F32_OP;
+
if (src & SLJIT_MEM) {
emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, dst_r, src, srcw);
src = dst_r;
@@ -1760,8 +1550,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
op ^= SLJIT_F32_OP;
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
@@ -1806,21 +1594,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
if (FAST_IS_REG(dst))
- return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG3));
+ return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG2));
/* Memory. */
- if (getput_arg_fast(compiler, WORD_SIZE | STORE, TMP_REG3, dst, dstw))
- return compiler->error;
- /* TMP_REG3 is used for caching. */
- FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, TMP_REG3)));
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, 0, 0);
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, TMP_REG1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
@@ -1829,21 +1609,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
CHECK(check_sljit_emit_fast_return(compiler, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
if (FAST_IS_REG(src))
- FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, src)));
- else if (src & SLJIT_MEM) {
- if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG3, src, srcw))
- FAIL_IF(compiler->error);
- else {
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, 0, 0));
- FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, TMP_REG2)));
- }
- }
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, TMP_REG3, srcw));
- return push_inst16(compiler, BLX | RN3(TMP_REG3));
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src, srcw, TMP_REG2));
+
+ return push_inst16(compiler, BX | RN3(TMP_REG2));
}
/* --------------------------------------------------------------------- */
@@ -1900,7 +1673,7 @@ static sljit_uw get_cc(sljit_s32 type)
return 0x7;
default: /* SLJIT_JUMP */
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return 0xe;
}
}
@@ -1934,7 +1707,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
type &= 0xff;
- /* In ARM, we don't need to touch the arguments. */
PTR_FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0));
if (type < SLJIT_JUMP) {
jump->flags |= IS_COND;
@@ -1954,6 +1726,241 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
return jump;
}
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 stack_offset = 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_offset = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u8 offsets[4];
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_f32);
+ arg_count++;
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_offset & 0x7)
+ stack_offset += sizeof(sljit_sw);
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_f64);
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_sw);
+ arg_count++;
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (stack_offset > 16)
+ FAIL_IF(push_inst16(compiler, SUB_SP | (((stack_offset - 16) + 0x7) & ~0x7) >> 2));
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 12);
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ float_arg_count--;
+ stack_offset = offsets[arg_count];
+
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (stack_offset << 10)));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800000 | RN4(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ arg_count--;
+ float_arg_count--;
+ stack_offset = offsets[arg_count];
+
+ SLJIT_ASSERT((stack_offset & 0x7) == 0);
+
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset || src_offset == stack_offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV2 | 0x100000 | (stack_offset << 10) | ((stack_offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800100 | RN4(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2)));
+ break;
+ default:
+ arg_count--;
+ word_arg_offset -= sizeof(sljit_sw);
+ stack_offset = offsets[arg_count];
+
+ SLJIT_ASSERT(stack_offset >= word_arg_offset);
+
+ if (stack_offset != word_arg_offset) {
+ if (stack_offset < 16) {
+ if (src_offset == stack_offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = 1 + (stack_offset >> 2);
+ src_offset = stack_offset;
+ }
+ FAIL_IF(push_inst16(compiler, MOV | (stack_offset >> 2) | (word_arg_offset << 1)));
+ } else
+ FAIL_IF(push_inst16(compiler, STR_SP | (word_arg_offset << 6) | ((stack_offset - 16) >> 2)));
+ }
+ break;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_s32 stack_size = 0;
+
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst32(compiler, VMOV | (0 << 16) | (0 << 12)));
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst32(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_size & 0x7)
+ stack_size += sizeof(sljit_sw);
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ stack_size += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (stack_size <= 16)
+ return SLJIT_SUCCESS;
+
+ return push_inst16(compiler, ADD_SP | ((((stack_size - 16) + 0x7) & ~0x7) >> 2));
+}
+
+#else
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 remap = 0;
+ sljit_u32 offset = 0;
+ sljit_u32 new_offset, mask;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) {
+ new_offset = 0;
+ mask = 1;
+
+ while (remap & mask) {
+ new_offset++;
+ mask <<= 1;
+ }
+ remap |= mask;
+
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | DD4((new_offset >> 1) + 1)
+ | ((new_offset & 0x1) ? 0x400000 : 0) | DM4((offset >> 1) + 1)));
+
+ offset += 2;
+ }
+ else if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) {
+ new_offset = 0;
+ mask = 3;
+
+ while (remap & mask) {
+ new_offset += 2;
+ mask <<= 2;
+ }
+ remap |= mask;
+
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | SLJIT_F32_OP | DD4((new_offset >> 1) + 1) | DM4((offset >> 1) + 1)));
+
+ offset += 2;
+ }
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+#else
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+#endif
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
{
struct sljit_jump *jump;
@@ -1962,16 +1969,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
- /* In ARM, we don't need to touch the arguments. */
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
if (!(src & SLJIT_IMM)) {
- if (FAST_IS_REG(src))
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src));
+ }
- FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw, TMP_REG1));
if (type >= SLJIT_FAST_CALL)
return push_inst16(compiler, BLX | RN3(TMP_REG1));
}
+ /* These jumps are converted to jump/call instructions when possible. */
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF(!jump);
set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
@@ -1982,25 +1993,55 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(TMP_REG1));
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+#ifdef __SOFTFP__
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ return softfloat_post_call_with_args(compiler, arg_types);
+#else /* !__SOFTFP__ */
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_ijump(compiler, type, src, srcw);
+#endif /* __SOFTFP__ */
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
sljit_s32 dst_r, flags = GET_ALL_FLAGS(op);
- sljit_ins cc, ins;
+ sljit_ins cc;
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- ADJUST_LOCAL_OFFSET(src, srcw);
-
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
op = GET_OPCODE(op);
cc = get_cc(type & 0xff);
- dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
if (op < SLJIT_ADD) {
FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
@@ -2008,60 +2049,141 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 1));
FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 0));
} else {
+ /* The movsi (immediate) instruction does not set flags in IT block. */
FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 1));
FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 0));
}
- if (dst_r != TMP_REG2)
+ if (!(dst & SLJIT_MEM))
return SLJIT_SUCCESS;
- return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw);
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2);
}
- ins = (op == SLJIT_AND ? ANDI : (op == SLJIT_OR ? ORRI : EORI));
- if ((op == SLJIT_OR || op == SLJIT_XOR) && FAST_IS_REG(dst) && dst == src) {
- /* Does not change the other bits. */
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (op == SLJIT_AND) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 1));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 0));
+ }
+ else {
FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
- FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst) | 1));
- if (flags & SLJIT_SET_E) {
- /* The condition must always be set, even if the ORRI/EORI is not executed above. */
- if (reg_map[dst] <= 7)
- return push_inst16(compiler, MOVS | RD3(TMP_REG1) | RN3(dst));
- return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst));
- }
+ FAIL_IF(push_inst32(compiler, ((op == SLJIT_OR) ? ORRI : EORI) | RN4(dst_r) | RD4(dst_r) | 1));
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (!(flags & SLJIT_SET_Z))
return SLJIT_SUCCESS;
+
+ /* The condition must always be set, even if the ORR/EORI is not executed above. */
+ return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ dst_reg &= ~SLJIT_I32_OP;
+
+ cc = get_cc(type & 0xff);
+
+ if (!(src & SLJIT_IMM)) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst16(compiler, MOV | SET_REGS44(dst_reg, src));
}
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, WORD_SIZE, TMP_REG2, src, srcw, dst, dstw));
- src = TMP_REG2;
- srcw = 0;
- } else if (src & SLJIT_IMM) {
- FAIL_IF(load_immediate(compiler, TMP_REG2, srcw));
- src = TMP_REG2;
- srcw = 0;
+ tmp = (sljit_uw) srcw;
+
+ if (tmp < 0x10000) {
+ /* set low 16 bits, set hi 16 bits to 0. */
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff));
}
- if (op == SLJIT_AND || src != dst_r) {
- FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
- FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 1));
- FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 0));
+ tmp = get_imm(srcw);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOV_WI | RD4(dst_reg) | tmp);
}
- else {
+
+ tmp = get_imm(~srcw);
+ if (tmp != INVALID_IMM) {
FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
- FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 1));
+ return push_inst32(compiler, MVN_WI | RD4(dst_reg) | tmp);
}
- if (dst_r == TMP_REG2)
- FAIL_IF(emit_op_mem2(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, 0, 0));
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | ((cc & 0x1) << 3) | 0x4));
+
+ tmp = (sljit_uw) srcw;
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst_reg)
+ | COPY_BITS(tmp, 12 + 16, 16, 4) | COPY_BITS(tmp, 11 + 16, 26, 1) | COPY_BITS(tmp, 8 + 16, 12, 3) | ((tmp & 0xff0000) >> 16));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -255))
+ return SLJIT_ERR_UNSUPPORTED;
- if (flags & SLJIT_SET_E) {
- /* The condition must always be set, even if the ORR/EORI is not executed above. */
- if (reg_map[dst_r] <= 7)
- return push_inst16(compiler, MOVS | RD3(TMP_REG1) | RN3(dst_r));
- return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r));
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
}
- return SLJIT_SUCCESS;
+
+ if (type & SLJIT_MEM_STORE)
+ flags |= STORE;
+
+ inst = sljit_mem32[flags] | 0x900;
+
+ if (type & SLJIT_MEM_PRE)
+ inst |= 0x400;
+
+ if (memw >= 0)
+ inst |= 0x200;
+ else
+ memw = -memw;
+
+ return push_inst32(compiler, inst | RT4(reg) | RN4(mem & REG_MASK) | memw);
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
@@ -2077,11 +2199,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
PTR_FAIL_IF(!const_);
set_const(const_, compiler);
- dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, init_value));
if (dst & SLJIT_MEM)
- PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw));
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
return const_;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c
index b15a57dfdb..9f9e157a05 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -40,35 +40,37 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a
#define EMIT_LOGICAL(op_imm, op_norm) \
if (flags & SRC2_IMM) { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \
} \
else { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \
}
#define EMIT_SHIFT(op_imm, op_v) \
if (flags & SRC2_IMM) { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \
} \
else { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst))); \
}
static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
{
+ sljit_s32 is_overflow, is_carry, is_handled;
+
switch (GET_OPCODE(op)) {
case SLJIT_MOV:
case SLJIT_MOV_U32:
@@ -93,8 +95,9 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
}
return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst));
}
- else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
return SLJIT_SUCCESS;
case SLJIT_MOV_U16:
@@ -111,24 +114,25 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
}
return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst));
}
- else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
return SLJIT_SUCCESS;
case SLJIT_NOT:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
- if (op & SLJIT_SET_E)
+ if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST))
FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst)));
return SLJIT_SUCCESS;
case SLJIT_CLZ:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
- if (op & SLJIT_SET_E)
+ if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, CLZ | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST))
FAIL_IF(push_inst(compiler, CLZ | S(src2) | T(dst) | D(dst), DR(dst)));
#else
if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) {
@@ -145,130 +149,192 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
FAIL_IF(push_inst(compiler, ADDIU | S(dst) | T(dst) | IMM(1), DR(dst)));
FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS));
FAIL_IF(push_inst(compiler, SLL | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS));
- if (op & SLJIT_SET_E)
- return push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG);
#endif
return SLJIT_SUCCESS;
case SLJIT_ADD:
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_O) {
+ if (is_overflow) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
else
- FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
- if (op & SLJIT_SET_E)
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O)) {
+
+ if (is_overflow || is_carry) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
else {
- FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
- FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
}
}
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_O)
- FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- if (op & SLJIT_SET_E)
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst)));
}
/* a + b >= a | b (otherwise, the carry should be set to 1). */
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
- if (!(op & SLJIT_SET_O))
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (!is_overflow)
return SLJIT_SUCCESS;
- FAIL_IF(push_inst(compiler, SLL | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- return push_inst(compiler, SLL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG);
case SLJIT_ADDC:
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_C) {
+ if (is_carry) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
else {
- FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
}
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst)));
} else {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
- FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
- if (!(op & SLJIT_SET_C))
+ FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+ if (!is_carry)
return SLJIT_SUCCESS;
- /* Set ULESS_FLAG (dst == 0) && (ULESS_FLAG == 1). */
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
/* Set carry flag. */
- return push_inst(compiler, OR | SA(ULESS_FLAG) | TA(OVERFLOW_FLAG) | DA(ULESS_FLAG), ULESS_FLAG);
+ return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
case SLJIT_SUB:
- if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_U | SLJIT_SET_S)) || src2 == SIMM_MIN)) {
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
src2 = TMP_REG2;
flags &= ~SRC2_IMM;
}
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL)
+ {
+ FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL)
+ {
+ FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_O) {
+ if (is_overflow) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
else
- FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
- if (op & SLJIT_SET_E)
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_O)
- FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- if (op & SLJIT_SET_E)
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (op & (SLJIT_SET_U | SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
- if (op & SLJIT_SET_U)
- FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG));
- if (op & SLJIT_SET_S) {
- FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG));
- FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG));
- }
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (!(op & SLJIT_SET_O))
+ if (!is_overflow)
return SLJIT_SUCCESS;
- FAIL_IF(push_inst(compiler, SLL | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- return push_inst(compiler, SRL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG);
case SLJIT_SUBC:
if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
@@ -277,28 +343,31 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
flags &= ~SRC2_IMM;
}
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(LESS_FLAG), LESS_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
- return (op & SLJIT_SET_C) ? push_inst(compiler, OR | SA(OVERFLOW_FLAG) | TA(LESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG) : SLJIT_SUCCESS;
+ FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+ return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS;
case SLJIT_MUL:
SLJIT_ASSERT(!(flags & SRC2_IMM));
- if (!(op & SLJIT_SET_O)) {
+
+ if (GET_FLAG_TYPE(op) != SLJIT_MUL_OVERFLOW) {
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
#else
@@ -307,10 +376,10 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
#endif
}
FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS));
- FAIL_IF(push_inst(compiler, MFHI | DA(ULESS_FLAG), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG));
FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst)));
- FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(UGREATER_FLAG) | SH_IMM(31), UGREATER_FLAG));
- return push_inst(compiler, SUBU | SA(ULESS_FLAG) | TA(UGREATER_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG));
+ return push_inst(compiler, SUBU | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
case SLJIT_AND:
EMIT_LOGICAL(ANDI, AND);
@@ -337,7 +406,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return SLJIT_SUCCESS;
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
@@ -366,3 +435,232 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
SLJIT_CACHE_FLUSH(inst, inst + 2);
}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr)
+{
+ sljit_s32 stack_offset = 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 arg_count_save, types_save;
+ sljit_ins prev_ins = NOP;
+ sljit_ins ins = NOP;
+ sljit_u8 offsets[4];
+
+ SLJIT_ASSERT(reg_map[TMP_REG3] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+
+ if (word_arg_count == 0 && arg_count <= 1)
+ offsets[arg_count] = 254 + arg_count;
+
+ stack_offset += sizeof(sljit_f32);
+ arg_count++;
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_offset & 0x7)
+ stack_offset += sizeof(sljit_sw);
+ offsets[arg_count] = (sljit_u8)stack_offset;
+
+ if (word_arg_count == 0 && arg_count <= 1)
+ offsets[arg_count] = 254 + arg_count;
+
+ stack_offset += sizeof(sljit_f64);
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ offsets[arg_count] = (sljit_u8)stack_offset;
+ stack_offset += sizeof(sljit_sw);
+ arg_count++;
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ /* Stack is aligned to 16 bytes, max two doubles can be placed on the stack. */
+ if (stack_offset > 16)
+ FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-16), DR(SLJIT_SP)));
+
+ types_save = types;
+ arg_count_save = arg_count;
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ if (offsets[arg_count] < 254)
+ ins = SWC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(offsets[arg_count]);
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ arg_count--;
+ if (offsets[arg_count] < 254)
+ ins = SDC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(offsets[arg_count]);
+ float_arg_count--;
+ break;
+ default:
+ if (offsets[arg_count - 1] >= 16)
+ ins = SW | S(SLJIT_SP) | T(word_arg_count) | IMM(offsets[arg_count - 1]);
+ else if (arg_count != word_arg_count)
+ ins = ADDU | S(word_arg_count) | TA(0) | DA(4 + (offsets[arg_count - 1] >> 2));
+ else if (arg_count == 1)
+ ins = ADDU | S(SLJIT_R0) | TA(0) | D(TMP_REG3);
+
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
+ types = types_save;
+ arg_count = arg_count_save;
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ if (offsets[arg_count] == 254)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ else if (offsets[arg_count] < 16)
+ ins = LW | S(SLJIT_SP) | TA(4 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count]);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ arg_count--;
+ if (offsets[arg_count] == 254)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ else if (offsets[arg_count] < 16) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = LW | S(SLJIT_SP) | TA(4 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count]);
+ ins = LW | S(SLJIT_SP) | TA(5 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count] + sizeof(sljit_sw));
+ }
+ break;
+ default:
+ arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_s32 stack_offset = 0;
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ stack_offset += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (stack_offset & 0x7)
+ stack_offset += sizeof(sljit_sw);
+ stack_offset += sizeof(sljit_f64);
+ break;
+ default:
+ stack_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ /* Stack is aligned to 16 bytes, max two doubles can be placed on the stack. */
+ if (stack_offset > 16)
+ return push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(16), DR(SLJIT_SP));
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_ins ins;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0));
+
+ jump->flags |= IS_JAL | IS_CALL;
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ PTR_FAIL_IF(post_call_with_args(compiler, arg_types));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ }
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ /* Register input. */
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+ return post_call_with_args(compiler, arg_types);
+}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c
index 8b96d5b73d..ff6f048659 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -123,15 +123,15 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a
#define EMIT_LOGICAL(op_imm, op_norm) \
if (flags & SRC2_IMM) { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \
} \
else { \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \
}
@@ -144,16 +144,16 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a
} \
else \
ins = (op & SLJIT_I32_OP) ? op_imm : op_dimm; \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \
} \
else { \
ins = (op & SLJIT_I32_OP) ? op_v : op_dv; \
- if (op & SLJIT_SET_E) \
+ if (op & SLJIT_SET_Z) \
FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
- if (CHECK_FLAGS(SLJIT_SET_E)) \
+ if (!(flags & UNUSED_DEST)) \
FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst))); \
}
@@ -161,6 +161,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
{
sljit_ins ins;
+ sljit_s32 is_overflow, is_carry, is_handled;
switch (GET_OPCODE(op)) {
case SLJIT_MOV:
@@ -180,8 +181,9 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
}
return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst));
}
- else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
return SLJIT_SUCCESS;
case SLJIT_MOV_U16:
@@ -194,8 +196,9 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
}
return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst));
}
- else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
return SLJIT_SUCCESS;
case SLJIT_MOV_U32:
@@ -209,18 +212,18 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
case SLJIT_NOT:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
- if (op & SLJIT_SET_E)
+ if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST))
FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst)));
return SLJIT_SUCCESS;
case SLJIT_CLZ:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
- if (op & SLJIT_SET_E)
+ if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST))
FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst)));
#else
if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) {
@@ -237,130 +240,192 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(dst) | IMM(1), DR(dst)));
FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS));
FAIL_IF(push_inst(compiler, SELECT_OP(DSLL, SLL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS));
- if (op & SLJIT_SET_E)
- return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG);
#endif
return SLJIT_SUCCESS;
case SLJIT_ADD:
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_O) {
+ if (is_overflow) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
else
- FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
- if (op & SLJIT_SET_E)
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O)) {
+
+ if (is_overflow || is_carry) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
else {
- FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
- FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
}
}
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_O)
- FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- if (op & SLJIT_SET_E)
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
}
/* a + b >= a | b (otherwise, the carry should be set to 1). */
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
- if (!(op & SLJIT_SET_O))
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (!is_overflow)
return SLJIT_SUCCESS;
- FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- return push_inst(compiler, SELECT_OP(DSRL32, SLL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG);
case SLJIT_ADDC:
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_C) {
+ if (is_carry) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
else {
- FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
}
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
} else {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
- FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
- if (!(op & SLJIT_SET_C))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+ if (!is_carry)
return SLJIT_SUCCESS;
- /* Set ULESS_FLAG (dst == 0) && (ULESS_FLAG == 1). */
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
/* Set carry flag. */
- return push_inst(compiler, OR | SA(ULESS_FLAG) | TA(OVERFLOW_FLAG) | DA(ULESS_FLAG), ULESS_FLAG);
+ return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
case SLJIT_SUB:
- if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_U | SLJIT_SET_S)) || src2 == SIMM_MIN)) {
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
src2 = TMP_REG2;
flags &= ~SRC2_IMM;
}
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL)
+ {
+ FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL)
+ {
+ FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_O) {
+ if (is_overflow) {
if (src2 >= 0)
- FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
else
- FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
}
- if (op & SLJIT_SET_E)
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
- if (op & (SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_O)
- FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- if (op & SLJIT_SET_E)
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
- if (op & (SLJIT_SET_U | SLJIT_SET_C | SLJIT_SET_O))
- FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
- if (op & SLJIT_SET_U)
- FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG));
- if (op & SLJIT_SET_S) {
- FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG));
- FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG));
- }
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
/* dst may be the same as src1 or src2. */
- if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (!(op & SLJIT_SET_O))
+ if (!is_overflow)
return SLJIT_SUCCESS;
- FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
- return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG);
case SLJIT_SUBC:
if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
@@ -369,28 +434,31 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
flags &= ~SRC2_IMM;
}
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
if (flags & SRC2_IMM) {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
}
else {
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
/* dst may be the same as src1 or src2. */
FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
}
- if (op & SLJIT_SET_C)
- FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(LESS_FLAG), LESS_FLAG));
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
- FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
- return (op & SLJIT_SET_C) ? push_inst(compiler, OR | SA(OVERFLOW_FLAG) | TA(LESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG) : SLJIT_SUCCESS;
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+ return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS;
case SLJIT_MUL:
SLJIT_ASSERT(!(flags & SRC2_IMM));
- if (!(op & SLJIT_SET_O)) {
+
+ if (GET_FLAG_TYPE(op) != SLJIT_MUL_OVERFLOW) {
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
if (op & SLJIT_I32_OP)
return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
@@ -402,10 +470,10 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
#endif
}
FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
- FAIL_IF(push_inst(compiler, MFHI | DA(ULESS_FLAG), ULESS_FLAG));
+ FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG));
FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst)));
- FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(UGREATER_FLAG) | SH_IMM(31), UGREATER_FLAG));
- return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(ULESS_FLAG) | TA(UGREATER_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG);
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG));
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
case SLJIT_AND:
EMIT_LOGICAL(ANDI, AND);
@@ -432,7 +500,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return SLJIT_SUCCESS;
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
@@ -469,3 +537,132 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta
inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
SLJIT_CACHE_FLUSH(inst, inst + 6);
}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_ins prev_ins = NOP;
+ sljit_ins ins = NOP;
+
+ SLJIT_ASSERT(reg_map[TMP_REG3] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ case SLJIT_ARG_TYPE_F64:
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_S | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_D | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ ins = DADDU | S(word_arg_count) | TA(0) | D(arg_count);
+ else if (arg_count == 1)
+ ins = DADDU | S(SLJIT_R0) | TA(0) | D(TMP_REG3);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_ins ins;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0));
+
+ jump->flags |= IS_JAL | IS_CALL;
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ }
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ /* Register input. */
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c
index fe37e3ef00..e108433f70 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -57,21 +57,30 @@ typedef sljit_u32 sljit_ins;
#define RETURN_ADDR_REG 31
/* Flags are kept in volatile registers. */
-#define EQUAL_FLAG 12
-/* And carry flag as well. */
-#define ULESS_FLAG 13
-#define UGREATER_FLAG 14
-#define LESS_FLAG 15
-#define GREATER_FLAG 31
-#define OVERFLOW_FLAG 1
+#define EQUAL_FLAG 31
+#define OTHER_FLAG 1
-#define TMP_FREG1 (0)
-#define TMP_FREG2 ((SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) << 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
- 0, 2, 5, 6, 7, 8, 9, 10, 11, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 3, 25, 4
+ 0, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 3, 25, 4
};
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 14, 2, 4, 6, 8, 12, 10
+};
+
+#else
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 13, 14, 15, 16, 17, 12, 18
+};
+
+#endif
+
/* --------------------------------------------------------------------- */
/* Instrucion forms */
/* --------------------------------------------------------------------- */
@@ -79,21 +88,23 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define S(s) (reg_map[s] << 21)
#define T(t) (reg_map[t] << 16)
#define D(d) (reg_map[d] << 11)
+#define FT(t) (freg_map[t] << 16)
+#define FS(s) (freg_map[s] << 11)
+#define FD(d) (freg_map[d] << 6)
/* Absolute registers. */
#define SA(s) ((s) << 21)
#define TA(t) ((t) << 16)
#define DA(d) ((d) << 11)
-#define FT(t) ((t) << 16)
-#define FS(s) ((s) << 11)
-#define FD(d) ((d) << 6)
#define IMM(imm) ((imm) & 0xffff)
#define SH_IMM(imm) ((imm) << 6)
#define DR(dr) (reg_map[dr])
+#define FR(dr) (freg_map[dr])
#define HI(opcode) ((opcode) << 26)
#define LO(opcode) (opcode)
/* S = (16 << 21) D = (17 << 21) */
#define FMT_S (16 << 21)
+#define FMT_D (17 << 21)
#define ABS_S (HI(17) | FMT_S | LO(5))
#define ADD_S (HI(17) | FMT_S | LO(0))
@@ -158,6 +169,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define OR (HI(0) | LO(37))
#define ORI (HI(13))
#define SD (HI(63))
+#define SDC1 (HI(61))
#define SLT (HI(0) | LO(42))
#define SLTI (HI(10))
#define SLTIU (HI(11))
@@ -171,6 +183,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define SUB_S (HI(17) | FMT_S | LO(1))
#define SUBU (HI(0) | LO(35))
#define SW (HI(43))
+#define SWC1 (HI(57))
#define TRUNC_W_S (HI(17) | FMT_S | LO(13))
#define XOR (HI(0) | LO(38))
#define XORI (HI(14))
@@ -178,7 +191,13 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
#define CLZ (HI(28) | LO(32))
#define DCLZ (HI(28) | LO(36))
+#define MOVF (HI(0) | (0 << 16) | LO(1))
+#define MOVN (HI(0) | LO(11))
+#define MOVT (HI(0) | (1 << 16) | LO(1))
+#define MOVZ (HI(0) | LO(10))
#define MUL (HI(28) | LO(2))
+#define PREF (HI(51))
+#define PREFX (HI(19) | LO(15))
#define SEB (HI(31) | (16 << 6) | LO(32))
#define SEH (HI(31) | (24 << 6) | LO(32))
#endif
@@ -495,6 +514,32 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return code;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ sljit_sw fir = 0;
+
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif defined(__GNUC__)
+ asm ("cfc1 %0, $0" : "=r"(fir));
+ return (fir >> 22) & 0x1;
+#else
+#error "FIR check is not implemented for this architecture"
+#endif
+
+#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ return 1;
+#endif
+
+ default:
+ return fir;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Entry, exit */
/* --------------------------------------------------------------------- */
@@ -513,25 +558,20 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#define MEM_MASK 0x1f
-#define WRITE_BACK 0x00020
-#define ARG_TEST 0x00040
-#define ALT_KEEP_CACHE 0x00080
-#define CUMULATIVE_OP 0x00100
-#define LOGICAL_OP 0x00200
-#define IMM_OP 0x00400
-#define SRC2_IMM 0x00800
-
-#define UNUSED_DEST 0x01000
-#define REG_DEST 0x02000
-#define REG1_SOURCE 0x04000
-#define REG2_SOURCE 0x08000
-#define SLOW_SRC1 0x10000
-#define SLOW_SRC2 0x20000
-#define SLOW_DEST 0x40000
-
-/* Only these flags are set. UNUSED_DEST is not set when no flags should be set. */
-#define CHECK_FLAGS(list) \
- (!(flags & UNUSED_DEST) || (op & GET_FLAGS(~(list))))
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define LOGICAL_OP 0x00100
+#define IMM_OP 0x00200
+#define SRC2_IMM 0x00400
+
+#define UNUSED_DEST 0x00800
+#define REG_DEST 0x01000
+#define REG1_SOURCE 0x02000
+#define REG2_SOURCE 0x04000
+#define SLOW_SRC1 0x08000
+#define SLOW_SRC2 0x10000
+#define SLOW_DEST 0x20000
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
#define STACK_STORE SW
@@ -541,6 +581,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#define STACK_LOAD LD
#endif
+static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw);
+
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
#include "sljitNativeMIPS_32.c"
#else
@@ -548,15 +590,15 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#endif
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
sljit_ins base;
- sljit_s32 i, tmp, offs;
+ sljit_s32 args, i, tmp, offs;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
@@ -593,6 +635,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
FAIL_IF(push_inst(compiler, STACK_STORE | base | T(i) | IMM(offs), MOVABLE_INS));
}
+ args = get_arg_count(arg_types);
+
if (args >= 1)
FAIL_IF(push_inst(compiler, ADDU_W | SA(4) | TA(0) | D(SLJIT_S0), DR(SLJIT_S0)));
if (args >= 2)
@@ -604,12 +648,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
@@ -711,7 +755,7 @@ static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flag
{
SLJIT_ASSERT(arg & SLJIT_MEM);
- if ((!(flags & WRITE_BACK) || !(arg & REG_MASK)) && !(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
+ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
/* Works for both absoulte and relative addresses. */
if (SLJIT_UNLIKELY(flags & ARG_TEST))
return 1;
@@ -769,33 +813,21 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
argw &= 0x3;
- if ((flags & WRITE_BACK) && reg_ar == DR(base)) {
- SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar);
- FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
- reg_ar = DR(TMP_REG1);
- }
/* Using the cache. */
if (argw == compiler->cache_argw) {
- if (!(flags & WRITE_BACK)) {
- if (arg == compiler->cache_arg)
+ if (arg == compiler->cache_arg)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
+
+ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
- if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
- if (arg == next_arg && argw == (next_argw & 0x3)) {
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
- }
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar));
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
- }
- }
- else {
- if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base)));
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot);
}
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
}
}
@@ -805,55 +837,15 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl
FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG3) | SH_IMM(argw), DR(TMP_REG3)));
}
- if (!(flags & WRITE_BACK)) {
- if (arg == next_arg && argw == (next_argw & 0x3)) {
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
- tmp_ar = DR(TMP_REG3);
- }
- else
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar));
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
- }
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(base), DR(base)));
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot);
- }
-
- if (SLJIT_UNLIKELY(flags & WRITE_BACK) && base) {
- /* Update only applies if a base register exists. */
- if (reg_ar == DR(base)) {
- SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar);
- if (argw <= SIMM_MAX && argw >= SIMM_MIN) {
- FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar) | IMM(argw), MOVABLE_INS));
- if (argw)
- return push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base));
- return SLJIT_SUCCESS;
- }
- FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
- reg_ar = DR(TMP_REG1);
- }
-
- if (argw <= SIMM_MAX && argw >= SIMM_MIN) {
- if (argw)
- FAIL_IF(push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base)));
- }
- else {
- if (compiler->cache_arg == SLJIT_MEM && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) {
- if (argw != compiler->cache_argw) {
- FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3)));
- compiler->cache_argw = argw;
- }
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base)));
- }
- else {
- compiler->cache_arg = SLJIT_MEM;
- compiler->cache_argw = argw;
- FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw));
- FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base)));
- }
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
+ tmp_ar = DR(TMP_REG3);
}
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot);
+ else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
}
if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) {
@@ -923,15 +915,13 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
}
if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM))
- return SLJIT_SUCCESS;
- if (GET_FLAGS(op))
- flags |= UNUSED_DEST;
+ SLJIT_ASSERT(HAS_FLAGS(op));
+ flags |= UNUSED_DEST;
}
else if (FAST_IS_REG(dst)) {
dst_r = dst;
flags |= REG_DEST;
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P)
sugg_src2_r = dst_r;
}
else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, DR(TMP_REG1), dst, dstw))
@@ -985,7 +975,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
if (FAST_IS_REG(src2)) {
src2_r = src2;
flags |= REG2_SOURCE;
- if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
+ if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P)
dst_r = src2_r;
}
else if (src2 & SLJIT_IMM) {
@@ -996,7 +986,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
}
else {
src2_r = 0;
- if ((op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) && (dst & SLJIT_MEM))
+ if ((op >= SLJIT_MOV && op <= SLJIT_MOV_P) && (dst & SLJIT_MEM))
dst_r = 0;
}
}
@@ -1088,6 +1078,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
return SLJIT_SUCCESS;
}
+#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw <= SIMM_MAX && srcw >= SIMM_MIN)
+ return push_inst(compiler, PREF | S(src & REG_MASK) | IMM(srcw), MOVABLE_INS);
+
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ srcw &= 0x3;
+
+ if (SLJIT_UNLIKELY(srcw != 0)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(src)) | D(TMP_REG1) | SH_IMM(srcw), DR(TMP_REG1)));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(OFFS_REG(src)), MOVABLE_INS);
+}
+#endif
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
@@ -1103,12 +1116,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM))
+ return emit_prefetch(compiler, src, srcw);
+#endif
+ return SLJIT_SUCCESS;
+ }
+
#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
- if ((op & SLJIT_I32_OP) && GET_OPCODE(op) >= SLJIT_NOT) {
+ if ((op & SLJIT_I32_OP) && GET_OPCODE(op) >= SLJIT_NOT)
flags |= INT_DATA | SIGNED_DATA;
- if (src & SLJIT_IMM)
- srcw = (sljit_s32)srcw;
- }
#endif
switch (GET_OPCODE(op)) {
@@ -1142,36 +1160,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
case SLJIT_MOV_S16:
return emit_op(compiler, SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
- case SLJIT_MOVU:
- case SLJIT_MOVU_P:
- return emit_op(compiler, SLJIT_MOV, WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
- case SLJIT_MOVU_U32:
-#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
- return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-#else
- return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw);
-#endif
-
- case SLJIT_MOVU_S32:
-#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
- return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-#else
- return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw);
-#endif
-
- case SLJIT_MOVU_U8:
- return emit_op(compiler, SLJIT_MOV_U8, BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
-
- case SLJIT_MOVU_S8:
- return emit_op(compiler, SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
-
- case SLJIT_MOVU_U16:
- return emit_op(compiler, SLJIT_MOV_U16, HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
-
- case SLJIT_MOVU_S16:
- return emit_op(compiler, SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
-
case SLJIT_NOT:
return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
@@ -1182,6 +1170,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
}
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
@@ -1206,6 +1195,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
+
#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
if (op & SLJIT_I32_OP) {
flags |= INT_DATA | SIGNED_DATA;
@@ -1250,6 +1242,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
}
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
@@ -1266,7 +1259,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg << 1;
+ return FR(reg);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -1282,19 +1275,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#elif defined(__GNUC__)
- sljit_sw fir;
- asm ("cfc1 %0, $0" : "=r"(fir));
- return (fir >> 22) & 0x1;
-#else
-#error "FIR check is not implemented for this architecture"
-#endif
-}
-
#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 7))
#define FMT(op) (((op & SLJIT_F32_OP) ^ SLJIT_F32_OP) << (21 - 8))
@@ -1309,22 +1289,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
#endif
if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
src = TMP_FREG1;
}
- else
- src <<= 1;
FAIL_IF(push_inst(compiler, (TRUNC_W_S ^ (flags >> 19)) | FMT(op) | FS(src) | FD(TMP_FREG1), MOVABLE_INS));
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS);
/* Store the integer value from a VFP register. */
- return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, TMP_FREG1, dst, dstw, 0, 0);
+ return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, FR(TMP_FREG1), dst, dstw, 0, 0);
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
# undef is_long
@@ -1341,13 +1316,13 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
sljit_s32 flags = (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW) << 21;
#endif
- sljit_s32 dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, MTC1 | flags | T(src) | FS(TMP_FREG1), MOVABLE_INS));
else if (src & SLJIT_MEM) {
/* Load the integer value into a VFP register. */
- FAIL_IF(emit_op_mem2(compiler, ((flags) ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem2(compiler, ((flags) ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
}
else {
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
@@ -1361,7 +1336,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
FAIL_IF(push_inst(compiler, CVT_S_S | flags | (4 << 21) | (((op & SLJIT_F32_OP) ^ SLJIT_F32_OP) >> 8) | FS(TMP_FREG1) | FD(dst_r), MOVABLE_INS));
if (dst & SLJIT_MEM)
- return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG1), dst, dstw, 0, 0);
return SLJIT_SUCCESS;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
@@ -1373,39 +1348,38 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
+ sljit_ins inst;
+
if (src1 & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
src1 = TMP_FREG1;
}
- else
- src1 <<= 1;
if (src2 & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, 0, 0));
src2 = TMP_FREG2;
}
- else
- src2 <<= 1;
-
- /* src2 and src1 are swapped. */
- if (op & SLJIT_SET_E) {
- FAIL_IF(push_inst(compiler, C_UEQ_S | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS));
- FAIL_IF(push_inst(compiler, CFC1 | TA(EQUAL_FLAG) | DA(FCSR_REG), EQUAL_FLAG));
- FAIL_IF(push_inst(compiler, SRL | TA(EQUAL_FLAG) | DA(EQUAL_FLAG) | SH_IMM(23), EQUAL_FLAG));
- FAIL_IF(push_inst(compiler, ANDI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG));
- }
- if (op & SLJIT_SET_S) {
- /* Mixing the instructions for the two checks. */
- FAIL_IF(push_inst(compiler, C_ULT_S | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS));
- FAIL_IF(push_inst(compiler, CFC1 | TA(ULESS_FLAG) | DA(FCSR_REG), ULESS_FLAG));
- FAIL_IF(push_inst(compiler, C_ULT_S | FMT(op) | FT(src1) | FS(src2), UNMOVABLE_INS));
- FAIL_IF(push_inst(compiler, SRL | TA(ULESS_FLAG) | DA(ULESS_FLAG) | SH_IMM(23), ULESS_FLAG));
- FAIL_IF(push_inst(compiler, ANDI | SA(ULESS_FLAG) | TA(ULESS_FLAG) | IMM(1), ULESS_FLAG));
- FAIL_IF(push_inst(compiler, CFC1 | TA(UGREATER_FLAG) | DA(FCSR_REG), UGREATER_FLAG));
- FAIL_IF(push_inst(compiler, SRL | TA(UGREATER_FLAG) | DA(UGREATER_FLAG) | SH_IMM(23), UGREATER_FLAG));
- FAIL_IF(push_inst(compiler, ANDI | SA(UGREATER_FLAG) | TA(UGREATER_FLAG) | IMM(1), UGREATER_FLAG));
- }
- return push_inst(compiler, C_UN_S | FMT(op) | FT(src2) | FS(src1), FCSR_FCC);
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_EQUAL_F64:
+ case SLJIT_NOT_EQUAL_F64:
+ inst = C_UEQ_S;
+ break;
+ case SLJIT_LESS_F64:
+ case SLJIT_GREATER_EQUAL_F64:
+ inst = C_ULT_S;
+ break;
+ case SLJIT_GREATER_F64:
+ case SLJIT_LESS_EQUAL_F64:
+ inst = C_ULE_S;
+ break;
+ default:
+ SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED_F64 || GET_FLAG_TYPE(op) == SLJIT_ORDERED_F64);
+ inst = C_UN_S;
+ break;
+ }
+
+ return push_inst(compiler, inst | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
@@ -1424,14 +1398,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
op ^= SLJIT_F32_OP;
- dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(dst_r), src, srcw, dst, dstw));
src = dst_r;
}
- else
- src <<= 1;
switch (GET_OPCODE(op)) {
case SLJIT_MOV_F64:
@@ -1455,7 +1427,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
}
if (dst & SLJIT_MEM)
- return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0);
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(dst_r), dst, dstw, 0, 0);
return SLJIT_SUCCESS;
}
@@ -1475,42 +1447,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
compiler->cache_arg = 0;
compiler->cache_argw = 0;
- dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG2;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
if (src1 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w)) {
FAIL_IF(compiler->error);
src1 = TMP_FREG1;
} else
flags |= SLOW_SRC1;
}
- else
- src1 <<= 1;
if (src2 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w)) {
FAIL_IF(compiler->error);
src2 = TMP_FREG2;
} else
flags |= SLOW_SRC2;
}
- else
- src2 <<= 1;
if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
}
else {
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
}
}
else if (flags & SLOW_SRC1)
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
else if (flags & SLOW_SRC2)
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
if (flags & SLOW_SRC1)
src1 = TMP_FREG1;
@@ -1536,7 +1504,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
}
if (dst_r == TMP_FREG2)
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0));
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG2), dst, dstw, 0, 0));
return SLJIT_SUCCESS;
}
@@ -1551,10 +1519,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst(compiler, ADDU_W | SA(RETURN_ADDR_REG) | TA(0) | D(dst), DR(dst));
@@ -1570,10 +1534,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | DA(RETURN_ADDR_REG), RETURN_ADDR_REG));
- else if (src & SLJIT_MEM)
+ else
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw));
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, RETURN_ADDR_REG, srcw));
FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
return push_inst(compiler, NOP, UNMOVABLE_INS);
@@ -1643,55 +1605,39 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
switch (type) {
case SLJIT_EQUAL:
- case SLJIT_NOT_EQUAL_F64:
BR_NZ(EQUAL_FLAG);
break;
case SLJIT_NOT_EQUAL:
- case SLJIT_EQUAL_F64:
BR_Z(EQUAL_FLAG);
break;
case SLJIT_LESS:
- case SLJIT_LESS_F64:
- BR_Z(ULESS_FLAG);
- break;
- case SLJIT_GREATER_EQUAL:
- case SLJIT_GREATER_EQUAL_F64:
- BR_NZ(ULESS_FLAG);
- break;
case SLJIT_GREATER:
- case SLJIT_GREATER_F64:
- BR_Z(UGREATER_FLAG);
- break;
- case SLJIT_LESS_EQUAL:
- case SLJIT_LESS_EQUAL_F64:
- BR_NZ(UGREATER_FLAG);
- break;
case SLJIT_SIG_LESS:
- BR_Z(LESS_FLAG);
- break;
- case SLJIT_SIG_GREATER_EQUAL:
- BR_NZ(LESS_FLAG);
- break;
case SLJIT_SIG_GREATER:
- BR_Z(GREATER_FLAG);
- break;
- case SLJIT_SIG_LESS_EQUAL:
- BR_NZ(GREATER_FLAG);
- break;
case SLJIT_OVERFLOW:
case SLJIT_MUL_OVERFLOW:
- BR_Z(OVERFLOW_FLAG);
+ BR_Z(OTHER_FLAG);
break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
case SLJIT_NOT_OVERFLOW:
case SLJIT_MUL_NOT_OVERFLOW:
- BR_NZ(OVERFLOW_FLAG);
- break;
- case SLJIT_UNORDERED_F64:
- BR_F();
+ BR_NZ(OTHER_FLAG);
break;
+ case SLJIT_NOT_EQUAL_F64:
+ case SLJIT_GREATER_EQUAL_F64:
+ case SLJIT_GREATER_F64:
case SLJIT_ORDERED_F64:
BR_T();
break;
+ case SLJIT_EQUAL_F64:
+ case SLJIT_LESS_F64:
+ case SLJIT_LESS_EQUAL_F64:
+ case SLJIT_UNORDERED_F64:
+ BR_F();
+ break;
default:
/* Not conditional branch. */
inst = 0;
@@ -1706,19 +1652,16 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS));
PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0));
- if (type <= SLJIT_JUMP) {
+
+ if (type <= SLJIT_JUMP)
PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
- jump->addr = compiler->size;
- PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
- } else {
- SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
- /* Cannot be optimized out if type is >= CALL0. */
- jump->flags |= IS_JAL | (type >= SLJIT_CALL0 ? IS_CALL : 0);
+ else {
+ jump->flags |= IS_JAL;
PTR_FAIL_IF(push_inst(compiler, JALR | S(TMP_REG2) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
- jump->addr = compiler->size;
- /* A NOP if type < CALL1. */
- PTR_FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), UNMOVABLE_INS));
}
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
return jump;
}
@@ -1863,86 +1806,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler
#undef RESOLVE_IMM1
#undef RESOLVE_IMM2
-SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
- sljit_s32 src1, sljit_sw src1w,
- sljit_s32 src2, sljit_sw src2w)
-{
- struct sljit_jump *jump;
- sljit_ins inst;
- sljit_s32 if_true;
-
- CHECK_ERROR_PTR();
- CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w));
-
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
-
- if (src1 & SLJIT_MEM) {
- PTR_FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(type) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
- src1 = TMP_FREG1;
- }
- else
- src1 <<= 1;
-
- if (src2 & SLJIT_MEM) {
- PTR_FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(type) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
- src2 = TMP_FREG2;
- }
- else
- src2 <<= 1;
-
- jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
- PTR_FAIL_IF(!jump);
- set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
- jump->flags |= IS_BIT16_COND;
-
- switch (type & 0xff) {
- case SLJIT_EQUAL_F64:
- inst = C_UEQ_S;
- if_true = 1;
- break;
- case SLJIT_NOT_EQUAL_F64:
- inst = C_UEQ_S;
- if_true = 0;
- break;
- case SLJIT_LESS_F64:
- inst = C_ULT_S;
- if_true = 1;
- break;
- case SLJIT_GREATER_EQUAL_F64:
- inst = C_ULT_S;
- if_true = 0;
- break;
- case SLJIT_GREATER_F64:
- inst = C_ULE_S;
- if_true = 0;
- break;
- case SLJIT_LESS_EQUAL_F64:
- inst = C_ULE_S;
- if_true = 1;
- break;
- case SLJIT_UNORDERED_F64:
- inst = C_UN_S;
- if_true = 1;
- break;
- default: /* Make compilers happy. */
- SLJIT_ASSERT_STOP();
- case SLJIT_ORDERED_F64:
- inst = C_UN_S;
- if_true = 0;
- break;
- }
-
- PTR_FAIL_IF(push_inst(compiler, inst | FMT(type) | FT(src2) | FS(src1), UNMOVABLE_INS));
- /* Intentionally the other opcode. */
- PTR_FAIL_IF(push_inst(compiler, (if_true ? BC1F : BC1T) | JUMP_LENGTH, UNMOVABLE_INS));
- PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0));
- PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
- jump->addr = compiler->size;
- PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
- return jump;
-}
-
#undef JUMP_LENGTH
#undef BR_Z
#undef BR_NZ
@@ -1954,41 +1817,12 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 src_r = TMP_REG2;
struct sljit_jump *jump = NULL;
CHECK_ERROR();
CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
- if (FAST_IS_REG(src)) {
- if (DR(src) != 4)
- src_r = src;
- else
- FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
- }
-
- if (type >= SLJIT_CALL0) {
- SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
- if (src & (SLJIT_IMM | SLJIT_MEM)) {
- if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
- else {
- SLJIT_ASSERT(src_r == TMP_REG2 && (src & SLJIT_MEM));
- FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw));
- }
- FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
- /* We need an extra instruction in any case. */
- return push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), UNMOVABLE_INS);
- }
-
- /* Register input. */
- if (type >= SLJIT_CALL1)
- FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), 4));
- FAIL_IF(push_inst(compiler, JALR | S(src_r) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
- return push_inst(compiler, ADDU_W | S(src_r) | TA(0) | D(PIC_ADDR_REG), UNMOVABLE_INS);
- }
-
if (src & SLJIT_IMM) {
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF(!jump);
@@ -1999,11 +1833,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
jump->flags |= IS_MOVABLE;
FAIL_IF(emit_const(compiler, TMP_REG2, 0));
+ src = TMP_REG2;
+ }
+ else if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(TMP_REG2), src, srcw));
+ src = TMP_REG2;
}
- else if (src & SLJIT_MEM)
- FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw));
- FAIL_IF(push_inst(compiler, JR | S(src_r), UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS));
if (jump)
jump->addr = compiler->size;
FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
@@ -2012,115 +1849,160 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
- sljit_s32 sugg_dst_ar, dst_ar;
- sljit_s32 flags = GET_ALL_FLAGS(op);
+ sljit_s32 src_ar, dst_ar;
+ sljit_s32 saved_op = op;
#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
-# define mem_type WORD_DATA
+ sljit_s32 mem_type = WORD_DATA;
#else
sljit_s32 mem_type = (op & SLJIT_I32_OP) ? (INT_DATA | SIGNED_DATA) : WORD_DATA;
#endif
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
op = GET_OPCODE(op);
#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
- if (op == SLJIT_MOV_S32 || op == SLJIT_MOV_U32)
+ if (op == SLJIT_MOV_S32)
mem_type = INT_DATA | SIGNED_DATA;
#endif
- sugg_dst_ar = DR((op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2);
+ dst_ar = DR((op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2);
compiler->cache_arg = 0;
compiler->cache_argw = 0;
- if (op >= SLJIT_ADD && (src & SLJIT_MEM)) {
- ADJUST_LOCAL_OFFSET(src, srcw);
- FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), src, srcw, dst, dstw));
- src = TMP_REG1;
- srcw = 0;
- }
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), dst, dstw, dst, dstw));
switch (type & 0xff) {
case SLJIT_EQUAL:
case SLJIT_NOT_EQUAL:
- FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar));
- dst_ar = sugg_dst_ar;
- break;
- case SLJIT_LESS:
- case SLJIT_GREATER_EQUAL:
- case SLJIT_LESS_F64:
- case SLJIT_GREATER_EQUAL_F64:
- dst_ar = ULESS_FLAG;
- break;
- case SLJIT_GREATER:
- case SLJIT_LESS_EQUAL:
- case SLJIT_GREATER_F64:
- case SLJIT_LESS_EQUAL_F64:
- dst_ar = UGREATER_FLAG;
- break;
- case SLJIT_SIG_LESS:
- case SLJIT_SIG_GREATER_EQUAL:
- dst_ar = LESS_FLAG;
- break;
- case SLJIT_SIG_GREATER:
- case SLJIT_SIG_LESS_EQUAL:
- dst_ar = GREATER_FLAG;
- break;
- case SLJIT_OVERFLOW:
- case SLJIT_NOT_OVERFLOW:
- dst_ar = OVERFLOW_FLAG;
+ FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
break;
case SLJIT_MUL_OVERFLOW:
case SLJIT_MUL_NOT_OVERFLOW:
- FAIL_IF(push_inst(compiler, SLTIU | SA(OVERFLOW_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar));
- dst_ar = sugg_dst_ar;
+ FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
type ^= 0x1; /* Flip type bit for the XORI below. */
break;
+ case SLJIT_GREATER_F64:
+ case SLJIT_LESS_EQUAL_F64:
+ type ^= 0x1; /* Flip type bit for the XORI below. */
case SLJIT_EQUAL_F64:
case SLJIT_NOT_EQUAL_F64:
- dst_ar = EQUAL_FLAG;
- break;
-
+ case SLJIT_LESS_F64:
+ case SLJIT_GREATER_EQUAL_F64:
case SLJIT_UNORDERED_F64:
case SLJIT_ORDERED_F64:
- FAIL_IF(push_inst(compiler, CFC1 | TA(sugg_dst_ar) | DA(FCSR_REG), sugg_dst_ar));
- FAIL_IF(push_inst(compiler, SRL | TA(sugg_dst_ar) | DA(sugg_dst_ar) | SH_IMM(23), sugg_dst_ar));
- FAIL_IF(push_inst(compiler, ANDI | SA(sugg_dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar));
- dst_ar = sugg_dst_ar;
+ FAIL_IF(push_inst(compiler, CFC1 | TA(dst_ar) | DA(FCSR_REG), dst_ar));
+ FAIL_IF(push_inst(compiler, SRL | TA(dst_ar) | DA(dst_ar) | SH_IMM(23), dst_ar));
+ FAIL_IF(push_inst(compiler, ANDI | SA(dst_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
break;
default:
- SLJIT_ASSERT_STOP();
- dst_ar = sugg_dst_ar;
+ src_ar = OTHER_FLAG;
break;
}
if (type & 0x1) {
- FAIL_IF(push_inst(compiler, XORI | SA(dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar));
- dst_ar = sugg_dst_ar;
+ FAIL_IF(push_inst(compiler, XORI | SA(src_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
}
- if (op >= SLJIT_ADD) {
- if (DR(TMP_REG2) != dst_ar)
- FAIL_IF(push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
- return emit_op(compiler, op | flags, mem_type | CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0);
+ if (op < SLJIT_ADD) {
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_type, src_ar, dst, dstw);
+
+ if (src_ar != dst_ar)
+ return push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | DA(dst_ar), dst_ar);
+ return SLJIT_SUCCESS;
}
+ /* OTHER_FLAG cannot be specified as src2 argument at the moment. */
+ if (DR(TMP_REG2) != src_ar)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+
+ mem_type |= CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE;
+
if (dst & SLJIT_MEM)
- return emit_op_mem(compiler, mem_type, dst_ar, dst, dstw);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, TMP_REG2, 0);
+}
- if (sugg_dst_ar != dst_ar)
- return push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | DA(sugg_dst_ar), sugg_dst_ar);
- return SLJIT_SUCCESS;
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
+ sljit_ins ins;
+#endif
-#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
-# undef mem_type
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (dst_reg & SLJIT_I32_OP)
+ srcw = (sljit_s32)srcw;
+#endif
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ dst_reg &= ~SLJIT_I32_OP;
+
+ switch (type & 0xff) {
+ case SLJIT_EQUAL:
+ ins = MOVZ | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_NOT_EQUAL:
+ ins = MOVN | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ case SLJIT_MUL_OVERFLOW:
+ ins = MOVN | TA(OTHER_FLAG);
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ case SLJIT_MUL_NOT_OVERFLOW:
+ ins = MOVZ | TA(OTHER_FLAG);
+ break;
+ case SLJIT_EQUAL_F64:
+ case SLJIT_LESS_F64:
+ case SLJIT_LESS_EQUAL_F64:
+ case SLJIT_UNORDERED_F64:
+ ins = MOVT;
+ break;
+ case SLJIT_NOT_EQUAL_F64:
+ case SLJIT_GREATER_EQUAL_F64:
+ case SLJIT_GREATER_F64:
+ case SLJIT_ORDERED_F64:
+ ins = MOVF;
+ break;
+ default:
+ ins = MOVZ | TA(OTHER_FLAG);
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ return push_inst(compiler, ins | S(src) | D(dst_reg), DR(dst_reg));
+
+#else
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
#endif
}
@@ -2137,7 +2019,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
PTR_FAIL_IF(!const_);
set_const(const_, compiler);
- reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG2;
PTR_FAIL_IF(emit_const(compiler, reg, init_value));
diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c
index f696d1b8d5..fc185f7847 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -88,77 +88,86 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
case SLJIT_NEG:
SLJIT_ASSERT(src1 == TMP_REG1);
- return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2));
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, NEG | OE((flags & ALT_FORM1) ? ALT_SET_FLAGS : 0) | RC(flags) | D(dst) | A(src2));
case SLJIT_CLZ:
SLJIT_ASSERT(src1 == TMP_REG1);
- return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst));
+ return push_inst(compiler, CNTLZW | S(src2) | A(dst));
case SLJIT_ADD:
if (flags & ALT_FORM1) {
- /* Flags does not set: BIN_IMM_EXTS unnecessary. */
- SLJIT_ASSERT(src2 == TMP_REG2);
- return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm);
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
}
+
if (flags & ALT_FORM2) {
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
SLJIT_ASSERT(src2 == TMP_REG2);
- return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff));
}
if (flags & ALT_FORM3) {
SLJIT_ASSERT(src2 == TMP_REG2);
return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
}
- if (flags & ALT_FORM4) {
- /* Flags does not set: BIN_IMM_EXTS unnecessary. */
- FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)));
- return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)));
- }
if (!(flags & ALT_SET_FLAGS))
return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
- return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ if (flags & ALT_FORM4)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
case SLJIT_ADDC:
- if (flags & ALT_FORM1) {
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
- FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)));
- return push_inst(compiler, MTXER | S(0));
- }
return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
case SLJIT_SUB:
if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM3) {
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
SLJIT_ASSERT(src2 == TMP_REG2);
return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
}
- if (flags & (ALT_FORM2 | ALT_FORM3)) {
- SLJIT_ASSERT(src2 == TMP_REG2);
- if (flags & ALT_FORM2)
- FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm));
- if (flags & ALT_FORM3)
- return push_inst(compiler, CMPLI | CRD(4) | A(src1) | compiler->imm);
- return SLJIT_SUCCESS;
- }
- if (flags & (ALT_FORM4 | ALT_FORM5)) {
- if (flags & ALT_FORM4)
- FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2)));
- if (flags & ALT_FORM5)
- FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2)));
- return SLJIT_SUCCESS;
+
+ if (flags & ALT_FORM4) {
+ if (flags & ALT_FORM5) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm);
+ }
+ return push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2));
}
+
if (!(flags & ALT_SET_FLAGS))
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
- if (flags & ALT_FORM6)
- FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2)));
- return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, SUBF | RC(flags) | D(dst) | A(src2) | B(src1));
case SLJIT_SUBC:
- if (flags & ALT_FORM1) {
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
- FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)));
- return push_inst(compiler, MTXER | S(0));
- }
return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
case SLJIT_MUL:
@@ -166,7 +175,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
SLJIT_ASSERT(src2 == TMP_REG2);
return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
}
- return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
case SLJIT_AND:
if (flags & ALT_FORM1) {
@@ -228,19 +237,15 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2));
case SLJIT_ASHR:
- if (flags & ALT_FORM3)
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
if (flags & ALT_FORM1) {
SLJIT_ASSERT(src2 == TMP_REG2);
compiler->imm &= 0x1f;
- FAIL_IF(push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)));
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11));
}
- else
- FAIL_IF(push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2)));
- return (flags & ALT_FORM3) ? push_inst(compiler, MTXER | S(0)) : SLJIT_SUCCESS;
+ return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2));
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c
index 386d247dbc..706b2ba20b 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -204,84 +204,118 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
case SLJIT_NEG:
SLJIT_ASSERT(src1 == TMP_REG1);
+
+ if ((flags & (ALT_FORM1 | ALT_SIGN_EXT)) == (ALT_FORM1 | ALT_SIGN_EXT)) {
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1)));
+ FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(TMP_REG2)));
+ return push_inst(compiler, RLDI(dst, dst, 32, 32, 0));
+ }
+
UN_EXTS();
- return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2));
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, NEG | OE((flags & ALT_FORM1) ? ALT_SET_FLAGS : 0) | RC(flags) | D(dst) | A(src2));
case SLJIT_CLZ:
SLJIT_ASSERT(src1 == TMP_REG1);
if (flags & ALT_FORM1)
- return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst));
- return push_inst(compiler, CNTLZD | RC(flags) | S(src2) | A(dst));
+ return push_inst(compiler, CNTLZW | S(src2) | A(dst));
+ return push_inst(compiler, CNTLZD | S(src2) | A(dst));
case SLJIT_ADD:
if (flags & ALT_FORM1) {
- /* Flags does not set: BIN_IMM_EXTS unnecessary. */
- SLJIT_ASSERT(src2 == TMP_REG2);
- return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm);
+ if (flags & ALT_SIGN_EXT) {
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1)));
+ src1 = TMP_REG1;
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1)));
+ src2 = TMP_REG2;
+ }
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)));
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, RLDI(dst, dst, 32, 32, 0));
+ return SLJIT_SUCCESS;
}
+
if (flags & ALT_FORM2) {
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
SLJIT_ASSERT(src2 == TMP_REG2);
- return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff));
}
if (flags & ALT_FORM3) {
SLJIT_ASSERT(src2 == TMP_REG2);
BIN_IMM_EXTS();
return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
}
- if (flags & ALT_FORM4) {
- /* Flags does not set: BIN_IMM_EXTS unnecessary. */
- FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)));
- return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)));
- }
if (!(flags & ALT_SET_FLAGS))
return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
BIN_EXTS();
- return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ if (flags & ALT_FORM4)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
case SLJIT_ADDC:
- if (flags & ALT_FORM1) {
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
- FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)));
- return push_inst(compiler, MTXER | S(0));
- }
BIN_EXTS();
return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
case SLJIT_SUB:
if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ if (flags & ALT_SIGN_EXT) {
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1)));
+ src1 = TMP_REG1;
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1)));
+ src2 = TMP_REG2;
+ }
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ FAIL_IF(push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)));
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, RLDI(dst, dst, 32, 32, 0));
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & ALT_FORM3) {
/* Flags does not set: BIN_IMM_EXTS unnecessary. */
SLJIT_ASSERT(src2 == TMP_REG2);
return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
}
- if (flags & (ALT_FORM2 | ALT_FORM3)) {
- SLJIT_ASSERT(src2 == TMP_REG2);
- if (flags & ALT_FORM2)
- FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
- if (flags & ALT_FORM3)
- return push_inst(compiler, CMPLI | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm);
- return SLJIT_SUCCESS;
- }
- if (flags & (ALT_FORM4 | ALT_FORM5)) {
- if (flags & ALT_FORM4)
- FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
- if (flags & ALT_FORM5)
- return push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2));
- return SLJIT_SUCCESS;
+
+ if (flags & ALT_FORM4) {
+ if (flags & ALT_FORM5) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm);
+ }
+ return push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2));
}
+
if (!(flags & ALT_SET_FLAGS))
return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
BIN_EXTS();
- if (flags & ALT_FORM6)
- FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
- return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, SUBF | RC(flags) | D(dst) | A(src2) | B(src1));
case SLJIT_SUBC:
- if (flags & ALT_FORM1) {
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
- FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)));
- return push_inst(compiler, MTXER | S(0));
- }
BIN_EXTS();
return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
@@ -292,8 +326,8 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
}
BIN_EXTS();
if (flags & ALT_FORM2)
- return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1));
- return push_inst(compiler, MULLD | OERC(flags) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, MULLD | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
case SLJIT_AND:
if (flags & ALT_FORM1) {
@@ -345,10 +379,8 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
compiler->imm &= 0x1f;
return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1));
}
- else {
- compiler->imm &= 0x3f;
- return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags));
- }
+ compiler->imm &= 0x3f;
+ return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags));
}
return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2));
@@ -359,33 +391,80 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
compiler->imm &= 0x1f;
return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1));
}
- else {
- compiler->imm &= 0x3f;
- return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags));
- }
+ compiler->imm &= 0x3f;
+ return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags));
}
return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2));
case SLJIT_ASHR:
- if (flags & ALT_FORM3)
- FAIL_IF(push_inst(compiler, MFXER | D(0)));
if (flags & ALT_FORM1) {
SLJIT_ASSERT(src2 == TMP_REG2);
if (flags & ALT_FORM2) {
compiler->imm &= 0x1f;
- FAIL_IF(push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)));
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11));
}
- else {
- compiler->imm &= 0x3f;
- FAIL_IF(push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4)));
+ compiler->imm &= 0x3f;
+ return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4));
+ }
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 reg = 0;
+
+ if (src)
+ reg = *src & REG_MASK;
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ case SLJIT_ARG_TYPE_F64:
+ arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count && arg_count == reg) {
+ FAIL_IF(push_inst(compiler, OR | S(reg) | A(TMP_CALL_REG) | B(reg)));
+ *src = TMP_CALL_REG;
}
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ case SLJIT_ARG_TYPE_F64:
+ arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ FAIL_IF(push_inst(compiler, OR | S(word_arg_count) | A(arg_count) | B(word_arg_count)));
+
+ arg_count--;
+ word_arg_count--;
+ break;
}
- else
- FAIL_IF(push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2)));
- return (flags & ALT_FORM3) ? push_inst(compiler, MTXER | S(0)) : SLJIT_SUCCESS;
+
+ types >>= SLJIT_DEF_SHIFT;
}
- SLJIT_ASSERT_STOP();
return SLJIT_SUCCESS;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c
index 150c0bf9f4..5ef4ac96c4 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -93,20 +93,23 @@ static void ppc_cache_flush(sljit_ins *from, sljit_ins *to)
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
-#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
-#define TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 5)
+#define TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 4)
#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
-#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 6)
+#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 5)
#else
#define TMP_CALL_REG TMP_REG2
#endif
-#define TMP_FREG1 (0)
-#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
- 0, 3, 4, 5, 6, 7, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 8, 9, 10, 31, 12
+ 0, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 9, 10, 31, 12
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 1, 2, 3, 4, 5, 6, 0, 7
};
/* --------------------------------------------------------------------- */
@@ -117,19 +120,19 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
#define A(a) (reg_map[a] << 16)
#define B(b) (reg_map[b] << 11)
#define C(c) (reg_map[c] << 6)
-#define FD(fd) ((fd) << 21)
-#define FS(fs) ((fs) << 21)
-#define FA(fa) ((fa) << 16)
-#define FB(fb) ((fb) << 11)
-#define FC(fc) ((fc) << 6)
+#define FD(fd) (freg_map[fd] << 21)
+#define FS(fs) (freg_map[fs] << 21)
+#define FA(fa) (freg_map[fa] << 16)
+#define FB(fb) (freg_map[fb] << 11)
+#define FC(fc) (freg_map[fc] << 6)
#define IMM(imm) ((imm) & 0xffff)
#define CRD(d) ((d) << 21)
/* Instruction bit sections.
OE and Rc flag (see ALT_SET_FLAGS). */
-#define OERC(flags) (((flags & ALT_SET_FLAGS) >> 10) | (flags & ALT_SET_FLAGS))
+#define OE(flags) ((flags) & ALT_SET_FLAGS)
/* Rc flag (see ALT_SET_FLAGS). */
-#define RC(flags) ((flags & ALT_SET_FLAGS) >> 10)
+#define RC(flags) (((flags) & ALT_SET_FLAGS) >> 10)
#define HI(opcode) ((opcode) << 26)
#define LO(opcode) ((opcode) << 1)
@@ -154,6 +157,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
#define CMPL (HI(31) | LO(32))
#define CMPLI (HI(10))
#define CROR (HI(19) | LO(449))
+#define DCBT (HI(31) | LO(278))
#define DIVD (HI(31) | LO(489))
#define DIVDU (HI(31) | LO(457))
#define DIVW (HI(31) | LO(491))
@@ -524,6 +528,25 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#endif
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Entry, exit */
/* --------------------------------------------------------------------- */
@@ -533,47 +556,40 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
/* Creates an index in data_transfer_insts array. */
#define LOAD_DATA 0x01
#define INDEXED 0x02
-#define WRITE_BACK 0x04
+#define SIGNED_DATA 0x04
+
#define WORD_DATA 0x00
#define BYTE_DATA 0x08
#define HALF_DATA 0x10
#define INT_DATA 0x18
-#define SIGNED_DATA 0x20
/* Separates integer and floating point registers */
-#define GPR_REG 0x3f
-#define DOUBLE_DATA 0x40
+#define GPR_REG 0x1f
+#define DOUBLE_DATA 0x20
#define MEM_MASK 0x7f
/* Other inp_flags. */
-#define ARG_TEST 0x000100
/* Integer opertion and set flags -> requires exts on 64 bit systems. */
-#define ALT_SIGN_EXT 0x000200
+#define ALT_SIGN_EXT 0x000100
/* This flag affects the RC() and OERC() macros. */
#define ALT_SET_FLAGS 0x000400
-#define ALT_KEEP_CACHE 0x000800
-#define ALT_FORM1 0x010000
-#define ALT_FORM2 0x020000
-#define ALT_FORM3 0x040000
-#define ALT_FORM4 0x080000
-#define ALT_FORM5 0x100000
-#define ALT_FORM6 0x200000
+#define ALT_FORM1 0x001000
+#define ALT_FORM2 0x002000
+#define ALT_FORM3 0x004000
+#define ALT_FORM4 0x008000
+#define ALT_FORM5 0x010000
/* Source and destination is register. */
#define REG_DEST 0x000001
#define REG1_SOURCE 0x000002
#define REG2_SOURCE 0x000004
-/* getput_arg_fast returned true. */
-#define FAST_DEST 0x000008
-/* Multiple instructions are required. */
-#define SLOW_DEST 0x000010
/*
-ALT_SIGN_EXT 0x000200
-ALT_SET_FLAGS 0x000400
-ALT_FORM1 0x010000
+ALT_SIGN_EXT 0x000100
+ALT_SET_FLAGS 0x000200
+ALT_FORM1 0x001000
...
-ALT_FORM6 0x200000 */
+ALT_FORM5 0x010000 */
#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
#include "sljitNativePPC_32.c"
@@ -590,14 +606,14 @@ ALT_FORM6 0x200000 */
#endif
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 i, tmp, offs;
+ sljit_s32 args, i, tmp, offs;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
FAIL_IF(push_inst(compiler, MFLR | D(0)));
offs = -(sljit_s32)(sizeof(sljit_sw));
@@ -623,6 +639,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
#endif
FAIL_IF(push_inst(compiler, ADDI | D(TMP_ZERO) | A(0) | 0));
+
+ args = get_arg_count(arg_types);
+
if (args >= 1)
FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(SLJIT_S0) | B(SLJIT_R0)));
if (args >= 2)
@@ -654,12 +673,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET;
compiler->local_size = (local_size + 15) & ~0xf;
@@ -718,17 +737,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
/* Operators */
/* --------------------------------------------------------------------- */
-/* i/x - immediate/indexed form
- n/w - no write-back / write-back (1 bit)
- s/l - store/load (1 bit)
+/* s/l - store/load (1 bit)
+ i/x - immediate/indexed form
u/s - signed/unsigned (1 bit)
w/b/h/i - word/byte/half/int allowed (2 bit)
- It contans 32 items, but not all are different. */
+
+ Some opcodes are repeated (e.g. store signed / unsigned byte is the same instruction). */
/* 64 bit only: [reg+imm] must be aligned to 4 bytes. */
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
#define INT_ALIGNED 0x10000
-/* 64-bit only: there is no lwau instruction. */
-#define UPDATE_REQ 0x20000
+#endif
#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
#define ARCH_32_64(a, b) a
@@ -737,401 +756,217 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
#else
#define ARCH_32_64(a, b) b
#define INST_CODE_AND_DST(inst, flags, reg) \
- (((inst) & ~(INT_ALIGNED | UPDATE_REQ)) | (((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
+ (((inst) & ~INT_ALIGNED) | (((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
#endif
-static const sljit_ins data_transfer_insts[64 + 8] = {
+static const sljit_ins data_transfer_insts[64 + 16] = {
-/* -------- Unsigned -------- */
+/* -------- Integer -------- */
/* Word. */
-/* u w n i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
-/* u w n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
-/* u w n x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
-/* u w n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+/* w u i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w u i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
-/* u w w i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
-/* u w w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
-/* u w w x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
-/* u w w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+/* w s i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
/* Byte. */
-/* u b n i s */ HI(38) /* stb */,
-/* u b n i l */ HI(34) /* lbz */,
-/* u b n x s */ HI(31) | LO(215) /* stbx */,
-/* u b n x l */ HI(31) | LO(87) /* lbzx */,
+/* b u i s */ HI(38) /* stb */,
+/* b u i l */ HI(34) /* lbz */,
+/* b u x s */ HI(31) | LO(215) /* stbx */,
+/* b u x l */ HI(31) | LO(87) /* lbzx */,
-/* u b w i s */ HI(39) /* stbu */,
-/* u b w i l */ HI(35) /* lbzu */,
-/* u b w x s */ HI(31) | LO(247) /* stbux */,
-/* u b w x l */ HI(31) | LO(119) /* lbzux */,
+/* b s i s */ HI(38) /* stb */,
+/* b s i l */ HI(34) /* lbz */ /* EXTS_REQ */,
+/* b s x s */ HI(31) | LO(215) /* stbx */,
+/* b s x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */,
/* Half. */
-/* u h n i s */ HI(44) /* sth */,
-/* u h n i l */ HI(40) /* lhz */,
-/* u h n x s */ HI(31) | LO(407) /* sthx */,
-/* u h n x l */ HI(31) | LO(279) /* lhzx */,
+/* h u i s */ HI(44) /* sth */,
+/* h u i l */ HI(40) /* lhz */,
+/* h u x s */ HI(31) | LO(407) /* sthx */,
+/* h u x l */ HI(31) | LO(279) /* lhzx */,
-/* u h w i s */ HI(45) /* sthu */,
-/* u h w i l */ HI(41) /* lhzu */,
-/* u h w x s */ HI(31) | LO(439) /* sthux */,
-/* u h w x l */ HI(31) | LO(311) /* lhzux */,
+/* h s i s */ HI(44) /* sth */,
+/* h s i l */ HI(42) /* lha */,
+/* h s x s */ HI(31) | LO(407) /* sthx */,
+/* h s x l */ HI(31) | LO(343) /* lhax */,
/* Int. */
-/* u i n i s */ HI(36) /* stw */,
-/* u i n i l */ HI(32) /* lwz */,
-/* u i n x s */ HI(31) | LO(151) /* stwx */,
-/* u i n x l */ HI(31) | LO(23) /* lwzx */,
+/* i u i s */ HI(36) /* stw */,
+/* i u i l */ HI(32) /* lwz */,
+/* i u x s */ HI(31) | LO(151) /* stwx */,
+/* i u x l */ HI(31) | LO(23) /* lwzx */,
+
+/* i s i s */ HI(36) /* stw */,
+/* i s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */),
+/* i s x s */ HI(31) | LO(151) /* stwx */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */),
-/* u i w i s */ HI(37) /* stwu */,
-/* u i w i l */ HI(33) /* lwzu */,
-/* u i w x s */ HI(31) | LO(183) /* stwux */,
-/* u i w x l */ HI(31) | LO(55) /* lwzux */,
+/* -------- Floating point -------- */
-/* -------- Signed -------- */
+/* d i s */ HI(54) /* stfd */,
+/* d i l */ HI(50) /* lfd */,
+/* d x s */ HI(31) | LO(727) /* stfdx */,
+/* d x l */ HI(31) | LO(599) /* lfdx */,
+
+/* s i s */ HI(52) /* stfs */,
+/* s i l */ HI(48) /* lfs */,
+/* s x s */ HI(31) | LO(663) /* stfsx */,
+/* s x l */ HI(31) | LO(535) /* lfsx */,
+};
+
+static const sljit_ins updated_data_transfer_insts[64] = {
+
+/* -------- Integer -------- */
/* Word. */
-/* s w n i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
-/* s w n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
-/* s w n x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
-/* s w n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+/* w u i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w u i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
-/* s w w i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
-/* s w w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
-/* s w w x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
-/* s w w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+/* w s i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w s i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
/* Byte. */
-/* s b n i s */ HI(38) /* stb */,
-/* s b n i l */ HI(34) /* lbz */ /* EXTS_REQ */,
-/* s b n x s */ HI(31) | LO(215) /* stbx */,
-/* s b n x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */,
+/* b u i s */ HI(39) /* stbu */,
+/* b u i l */ HI(35) /* lbzu */,
+/* b u x s */ HI(31) | LO(247) /* stbux */,
+/* b u x l */ HI(31) | LO(119) /* lbzux */,
-/* s b w i s */ HI(39) /* stbu */,
-/* s b w i l */ HI(35) /* lbzu */ /* EXTS_REQ */,
-/* s b w x s */ HI(31) | LO(247) /* stbux */,
-/* s b w x l */ HI(31) | LO(119) /* lbzux */ /* EXTS_REQ */,
+/* b s i s */ HI(39) /* stbu */,
+/* b s i l */ 0 /* no such instruction */,
+/* b s x s */ HI(31) | LO(247) /* stbux */,
+/* b s x l */ 0 /* no such instruction */,
/* Half. */
-/* s h n i s */ HI(44) /* sth */,
-/* s h n i l */ HI(42) /* lha */,
-/* s h n x s */ HI(31) | LO(407) /* sthx */,
-/* s h n x l */ HI(31) | LO(343) /* lhax */,
+/* h u i s */ HI(45) /* sthu */,
+/* h u i l */ HI(41) /* lhzu */,
+/* h u x s */ HI(31) | LO(439) /* sthux */,
+/* h u x l */ HI(31) | LO(311) /* lhzux */,
-/* s h w i s */ HI(45) /* sthu */,
-/* s h w i l */ HI(43) /* lhau */,
-/* s h w x s */ HI(31) | LO(439) /* sthux */,
-/* s h w x l */ HI(31) | LO(375) /* lhaux */,
+/* h s i s */ HI(45) /* sthu */,
+/* h s i l */ HI(43) /* lhau */,
+/* h s x s */ HI(31) | LO(439) /* sthux */,
+/* h s x l */ HI(31) | LO(375) /* lhaux */,
/* Int. */
-/* s i n i s */ HI(36) /* stw */,
-/* s i n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */),
-/* s i n x s */ HI(31) | LO(151) /* stwx */,
-/* s i n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */),
+/* i u i s */ HI(37) /* stwu */,
+/* i u i l */ HI(33) /* lwzu */,
+/* i u x s */ HI(31) | LO(183) /* stwux */,
+/* i u x l */ HI(31) | LO(55) /* lwzux */,
-/* s i w i s */ HI(37) /* stwu */,
-/* s i w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | UPDATE_REQ | 0x2 /* lwa */),
-/* s i w x s */ HI(31) | LO(183) /* stwux */,
-/* s i w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */),
+/* i s i s */ HI(37) /* stwu */,
+/* i s i l */ ARCH_32_64(HI(33) /* lwzu */, 0 /* no such instruction */),
+/* i s x s */ HI(31) | LO(183) /* stwux */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */),
-/* -------- Double -------- */
+/* -------- Floating point -------- */
-/* d n i s */ HI(54) /* stfd */,
-/* d n i l */ HI(50) /* lfd */,
-/* d n x s */ HI(31) | LO(727) /* stfdx */,
-/* d n x l */ HI(31) | LO(599) /* lfdx */,
-
-/* s n i s */ HI(52) /* stfs */,
-/* s n i l */ HI(48) /* lfs */,
-/* s n x s */ HI(31) | LO(663) /* stfsx */,
-/* s n x l */ HI(31) | LO(535) /* lfsx */,
+/* d i s */ HI(55) /* stfdu */,
+/* d i l */ HI(51) /* lfdu */,
+/* d x s */ HI(31) | LO(759) /* stfdux */,
+/* d x l */ HI(31) | LO(631) /* lfdux */,
+/* s i s */ HI(53) /* stfsu */,
+/* s i l */ HI(49) /* lfsu */,
+/* s x s */ HI(31) | LO(695) /* stfsux */,
+/* s x l */ HI(31) | LO(567) /* lfsux */,
};
#undef ARCH_32_64
/* Simple cases, (no caching is required). */
-static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
{
sljit_ins inst;
+ sljit_s32 offs_reg;
+ sljit_sw high_short;
/* Should work when (arg & REG_MASK) == 0. */
- SLJIT_COMPILE_ASSERT(A(0) == 0, a0_must_be_0);
+ SLJIT_ASSERT(A(0) == 0);
SLJIT_ASSERT(arg & SLJIT_MEM);
- if (arg & OFFS_REG_MASK) {
- if (argw & 0x3)
- return 0;
- if (inp_flags & ARG_TEST)
- return 1;
-
- inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
- FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(OFFS_REG(arg))));
- return -1;
- }
-
- if (SLJIT_UNLIKELY(!(arg & REG_MASK)))
- inp_flags &= ~WRITE_BACK;
-
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- inst = data_transfer_insts[inp_flags & MEM_MASK];
- SLJIT_ASSERT((arg & REG_MASK) || !(inst & UPDATE_REQ));
-
- if (argw > SIMM_MAX || argw < SIMM_MIN || ((inst & INT_ALIGNED) && (argw & 0x3)) || (inst & UPDATE_REQ))
- return 0;
- if (inp_flags & ARG_TEST)
- return 1;
-#endif
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+ offs_reg = OFFS_REG(arg);
+ if (argw != 0) {
#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
- if (argw > SIMM_MAX || argw < SIMM_MIN)
- return 0;
- if (inp_flags & ARG_TEST)
- return 1;
-
- inst = data_transfer_insts[inp_flags & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
+ FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(arg)) | A(tmp_reg) | (argw << 11) | ((31 - argw) << 1)));
+#else
+ FAIL_IF(push_inst(compiler, RLDI(tmp_reg, OFFS_REG(arg), argw, 63 - argw, 1)));
#endif
+ offs_reg = tmp_reg;
+ }
- FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | IMM(argw)));
- return -1;
-}
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
-/* See getput_arg below.
- Note: can_cache is called only for binary operators. Those operator always
- uses word arguments without write back. */
-static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_sw high_short, next_high_short;
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- sljit_sw diff;
+ SLJIT_ASSERT(!(inst & INT_ALIGNED));
#endif
- SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));
-
- if (arg & OFFS_REG_MASK)
- return ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && (argw & 0x3) == (next_argw & 0x3));
-
- if (next_arg & OFFS_REG_MASK)
- return 0;
-
-#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
- high_short = (argw + ((argw & 0x8000) << 1)) & ~0xffff;
- next_high_short = (next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff;
- return high_short == next_high_short;
-#else
- if (argw <= 0x7fffffffl && argw >= -0x80000000l) {
- high_short = (argw + ((argw & 0x8000) << 1)) & ~0xffff;
- next_high_short = (next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff;
- if (high_short == next_high_short)
- return 1;
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(offs_reg));
}
- diff = argw - next_argw;
- if (!(arg & REG_MASK))
- return diff <= SIMM_MAX && diff >= SIMM_MIN;
-
- if (arg == next_arg && diff <= SIMM_MAX && diff >= SIMM_MIN)
- return 1;
-
- return 0;
-#endif
-}
-
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
-#define ADJUST_CACHED_IMM(imm) \
- if ((inst & INT_ALIGNED) && (imm & 0x3)) { \
- /* Adjust cached value. Fortunately this is really a rare case */ \
- compiler->cache_argw += imm & 0x3; \
- FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG3) | A(TMP_REG3) | (imm & 0x3))); \
- imm &= ~0x3; \
- }
-#endif
+ inst = data_transfer_insts[inp_flags & MEM_MASK];
+ arg &= REG_MASK;
-/* Emit the necessary instructions. See can_cache above. */
-static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
-{
- sljit_s32 tmp_r;
- sljit_ins inst;
- sljit_sw high_short, next_high_short;
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- sljit_sw diff;
-#endif
+ if ((inst & INT_ALIGNED) && (argw & 0x3) != 0) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
- SLJIT_ASSERT(arg & SLJIT_MEM);
-
- tmp_r = ((inp_flags & LOAD_DATA) && ((inp_flags) & MEM_MASK) <= GPR_REG) ? reg : TMP_REG1;
- /* Special case for "mov reg, [reg, ... ]". */
- if ((arg & REG_MASK) == tmp_r)
- tmp_r = TMP_REG1;
-
- if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
- argw &= 0x3;
- /* Otherwise getput_arg_fast would capture it. */
- SLJIT_ASSERT(argw);
-
- if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg && argw == compiler->cache_argw)
- tmp_r = TMP_REG3;
- else {
- if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == (next_argw & 0x3)) {
- compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
- compiler->cache_argw = argw;
- tmp_r = TMP_REG3;
- }
-#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
- FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(arg)) | A(tmp_r) | (argw << 11) | ((31 - argw) << 1)));
-#else
- FAIL_IF(push_inst(compiler, RLDI(tmp_r, OFFS_REG(arg), argw, 63 - argw, 1)));
-#endif
- }
inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(tmp_r));
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
}
+#endif
- if (SLJIT_UNLIKELY(!(arg & REG_MASK)))
- inp_flags &= ~WRITE_BACK;
-
- inst = data_transfer_insts[inp_flags & MEM_MASK];
- SLJIT_ASSERT((arg & REG_MASK) || !(inst & UPDATE_REQ));
+ if (argw <= SIMM_MAX && argw >= SIMM_MIN)
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | IMM(argw));
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- if (argw <= 0x7fff7fffl && argw >= -0x80000000l
- && (!(inst & INT_ALIGNED) || !(argw & 0x3)) && !(inst & UPDATE_REQ)) {
+ if (argw <= 0x7fff7fffl && argw >= -0x80000000l) {
#endif
- arg &= REG_MASK;
high_short = (sljit_s32)(argw + ((argw & 0x8000) << 1)) & ~0xffff;
- /* The getput_arg_fast should handle this otherwise. */
+
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
SLJIT_ASSERT(high_short && high_short <= 0x7fffffffl && high_short >= -0x80000000l);
#else
- SLJIT_ASSERT(high_short && !(inst & (INT_ALIGNED | UPDATE_REQ)));
+ SLJIT_ASSERT(high_short);
#endif
- if (inp_flags & WRITE_BACK) {
- if (arg == reg) {
- FAIL_IF(push_inst(compiler, OR | S(reg) | A(tmp_r) | B(reg)));
- reg = tmp_r;
- }
- tmp_r = arg;
- FAIL_IF(push_inst(compiler, ADDIS | D(arg) | A(arg) | IMM(high_short >> 16)));
- }
- else if (compiler->cache_arg != (SLJIT_MEM | arg) || high_short != compiler->cache_argw) {
- if ((next_arg & SLJIT_MEM) && !(next_arg & OFFS_REG_MASK)) {
- next_high_short = (sljit_s32)(next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff;
- if (high_short == next_high_short) {
- compiler->cache_arg = SLJIT_MEM | arg;
- compiler->cache_argw = high_short;
- tmp_r = TMP_REG3;
- }
- }
- FAIL_IF(push_inst(compiler, ADDIS | D(tmp_r) | A(arg & REG_MASK) | IMM(high_short >> 16)));
- }
- else
- tmp_r = TMP_REG3;
-
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_r) | IMM(argw));
+ FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM(high_short >> 16)));
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_reg) | IMM(argw));
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
}
- /* Everything else is PPC-64 only. */
- if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
- diff = argw - compiler->cache_argw;
- if ((compiler->cache_arg & SLJIT_IMM) && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- ADJUST_CACHED_IMM(diff);
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3) | IMM(diff));
- }
-
- diff = argw - next_argw;
- if ((next_arg & SLJIT_MEM) && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
-
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
- tmp_r = TMP_REG3;
- }
-
- FAIL_IF(load_immediate(compiler, tmp_r, argw));
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_r));
- }
+ /* The rest is PPC-64 only. */
- diff = argw - compiler->cache_argw;
- if (compiler->cache_arg == arg && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- SLJIT_ASSERT(!(inp_flags & WRITE_BACK) && !(inst & UPDATE_REQ));
- ADJUST_CACHED_IMM(diff);
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3) | IMM(diff));
- }
-
- if ((compiler->cache_arg & SLJIT_IMM) && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
- if (compiler->cache_argw != argw) {
- FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG3) | A(TMP_REG3) | IMM(diff)));
- compiler->cache_argw = argw;
- }
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(TMP_REG3));
- }
-
- if (argw == next_argw && (next_arg & SLJIT_MEM)) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
-
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
-
- inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(TMP_REG3));
- }
-
- diff = argw - next_argw;
- if (arg == next_arg && !(inp_flags & WRITE_BACK) && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
- FAIL_IF(push_inst(compiler, ADD | D(TMP_REG3) | A(TMP_REG3) | B(arg & REG_MASK)));
-
- compiler->cache_arg = arg;
- compiler->cache_argw = argw;
-
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3));
- }
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
- if ((next_arg & SLJIT_MEM) && !(next_arg & OFFS_REG_MASK) && diff <= SIMM_MAX && diff >= SIMM_MIN) {
- SLJIT_ASSERT(inp_flags & LOAD_DATA);
- FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
-
- compiler->cache_arg = SLJIT_IMM;
- compiler->cache_argw = argw;
- tmp_r = TMP_REG3;
- }
- else
- FAIL_IF(load_immediate(compiler, tmp_r, argw));
-
- /* Get the indexed version instead of the normal one. */
inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
- SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ)));
- return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(tmp_r));
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
#endif
}
-static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
-{
- if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
- return compiler->error;
- return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
-}
-
static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 input_flags,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
@@ -1139,42 +974,21 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
{
/* arg1 goes to TMP_REG1 or src reg
arg2 goes to TMP_REG2, imm or src reg
- TMP_REG3 can be used for caching
- result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
- sljit_s32 dst_r;
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
+ sljit_s32 dst_r = TMP_REG2;
sljit_s32 src1_r;
sljit_s32 src2_r;
sljit_s32 sugg_src2_r = TMP_REG2;
- sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_FORM6 | ALT_SIGN_EXT | ALT_SET_FLAGS);
-
- if (!(input_flags & ALT_KEEP_CACHE)) {
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- }
+ sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_SIGN_EXT | ALT_SET_FLAGS);
/* Destination check. */
- if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM))
- return SLJIT_SUCCESS;
- dst_r = TMP_REG2;
- }
- else if (FAST_IS_REG(dst)) {
+ if (SLOW_IS_REG(dst)) {
dst_r = dst;
flags |= REG_DEST;
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P)
sugg_src2_r = dst_r;
}
- else {
- SLJIT_ASSERT(dst & SLJIT_MEM);
- if (getput_arg_fast(compiler, input_flags | ARG_TEST, TMP_REG2, dst, dstw)) {
- flags |= FAST_DEST;
- dst_r = TMP_REG2;
- }
- else {
- flags |= SLOW_DEST;
- dst_r = 0;
- }
- }
/* Source 1. */
if (FAST_IS_REG(src1)) {
@@ -1185,80 +999,34 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
src1_r = TMP_REG1;
}
- else if (getput_arg_fast(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w)) {
- FAIL_IF(compiler->error);
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
src1_r = TMP_REG1;
}
- else
- src1_r = 0;
/* Source 2. */
if (FAST_IS_REG(src2)) {
src2_r = src2;
flags |= REG2_SOURCE;
- if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
+
+ if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P)
dst_r = src2_r;
}
else if (src2 & SLJIT_IMM) {
FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w));
src2_r = sugg_src2_r;
}
- else if (getput_arg_fast(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) {
- FAIL_IF(compiler->error);
- src2_r = sugg_src2_r;
- }
- else
- src2_r = 0;
-
- /* src1_r, src2_r and dst_r can be zero (=unprocessed).
- All arguments are complex addressing modes, and it is a binary operator. */
- if (src1_r == 0 && src2_r == 0 && dst_r == 0) {
- if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
- }
- else {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
- }
- src1_r = TMP_REG1;
- src2_r = TMP_REG2;
- }
- else if (src1_r == 0 && src2_r == 0) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
- src1_r = TMP_REG1;
- }
- else if (src1_r == 0 && dst_r == 0) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
- src1_r = TMP_REG1;
- }
- else if (src2_r == 0 && dst_r == 0) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
- src2_r = sugg_src2_r;
- }
-
- if (dst_r == 0)
- dst_r = TMP_REG2;
-
- if (src1_r == 0) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0));
- src1_r = TMP_REG1;
- }
-
- if (src2_r == 0) {
- FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0));
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, TMP_REG2));
src2_r = sugg_src2_r;
}
FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
- if (flags & (FAST_DEST | SLOW_DEST)) {
- if (flags & FAST_DEST)
- FAIL_IF(getput_arg_fast(compiler, input_flags, dst_r, dst, dstw));
- else
- FAIL_IF(getput_arg(compiler, input_flags, dst_r, dst, dstw, 0, 0));
- }
- return SLJIT_SUCCESS;
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, input_flags, dst_r, dst, dstw, TMP_REG1);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
@@ -1308,6 +1076,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
return SLJIT_SUCCESS;
}
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw == 0 && (src & REG_MASK) != SLJIT_UNUSED)
+ return push_inst(compiler, DCBT | A(0) | B(src & REG_MASK));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ /* Works with SLJIT_MEM0() case as well. */
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+ }
+
+ srcw &= 0x3;
+
+ if (srcw == 0)
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(OFFS_REG(src)));
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(src)) | A(TMP_REG1) | (srcw << 11) | ((31 - srcw) << 1)));
+#else
+ FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, OFFS_REG(src), srcw, 63 - srcw, 1)));
+#endif
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+}
+
#define EMIT_MOV(type, type_flags, type_cast) \
emit_op(compiler, (src & SLJIT_IMM) ? SLJIT_MOV : type, flags | (type_flags), dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? type_cast srcw : srcw)
@@ -1315,7 +1108,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
sljit_s32 op_flags = GET_ALL_FLAGS(op);
CHECK_ERROR();
@@ -1323,39 +1116,45 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM))
+ return emit_prefetch(compiler, src, srcw);
+
+ return SLJIT_SUCCESS;
+ }
+
op = GET_OPCODE(op);
if ((src & SLJIT_IMM) && srcw == 0)
src = TMP_ZERO;
- if (op_flags & SLJIT_SET_O)
+ if (GET_FLAG_TYPE(op_flags) == SLJIT_OVERFLOW)
FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+ if (op < SLJIT_NOT && FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
if (op_flags & SLJIT_I32_OP) {
if (op < SLJIT_NOT) {
- if (FAST_IS_REG(src) && src == dst) {
- if (!TYPE_CAST_NEEDED(op))
- return SLJIT_SUCCESS;
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
}
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- if (op == SLJIT_MOV_S32 && (src & SLJIT_MEM))
- op = SLJIT_MOV_U32;
- if (op == SLJIT_MOVU_S32 && (src & SLJIT_MEM))
- op = SLJIT_MOVU_U32;
- if (op == SLJIT_MOV_U32 && (src & SLJIT_IMM))
- op = SLJIT_MOV_S32;
- if (op == SLJIT_MOVU_U32 && (src & SLJIT_IMM))
- op = SLJIT_MOVU_S32;
-#endif
}
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
else {
/* Most operations expect sign extended arguments. */
flags |= INT_DATA | SIGNED_DATA;
- if (src & SLJIT_IMM)
- srcw = (sljit_s32)srcw;
+ if (HAS_FLAGS(op_flags))
+ flags |= ALT_SIGN_EXT;
}
-#endif
}
+#endif
switch (op) {
case SLJIT_MOV:
@@ -1386,39 +1185,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
case SLJIT_MOV_S16:
return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, (sljit_s16));
- case SLJIT_MOVU:
- case SLJIT_MOVU_P:
-#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
- case SLJIT_MOVU_U32:
- case SLJIT_MOVU_S32:
-#endif
- return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- case SLJIT_MOVU_U32:
- return EMIT_MOV(SLJIT_MOV_U32, INT_DATA | WRITE_BACK, (sljit_u32));
-
- case SLJIT_MOVU_S32:
- return EMIT_MOV(SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s32));
-#endif
-
- case SLJIT_MOVU_U8:
- return EMIT_MOV(SLJIT_MOV_U8, BYTE_DATA | WRITE_BACK, (sljit_u8));
-
- case SLJIT_MOVU_S8:
- return EMIT_MOV(SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s8));
-
- case SLJIT_MOVU_U16:
- return EMIT_MOV(SLJIT_MOV_U16, HALF_DATA | WRITE_BACK, (sljit_u16));
-
- case SLJIT_MOVU_S16:
- return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s16));
-
case SLJIT_NOT:
return emit_op(compiler, SLJIT_NOT, flags, dst, dstw, TMP_REG1, 0, src, srcw);
case SLJIT_NEG:
- return emit_op(compiler, SLJIT_NEG, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+ return emit_op(compiler, SLJIT_NEG, flags | (GET_FLAG_TYPE(op_flags) ? ALT_FORM1 : 0), dst, dstw, TMP_REG1, 0, src, srcw);
case SLJIT_CLZ:
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
@@ -1471,7 +1242,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- sljit_s32 flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
CHECK_ERROR();
CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
@@ -1479,6 +1250,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
+
if ((src1 & SLJIT_IMM) && src1w == 0)
src1 = TMP_ZERO;
if ((src2 & SLJIT_IMM) && src2w == 0)
@@ -1492,45 +1266,46 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
src1w = (sljit_s32)(src1w);
if (src2 & SLJIT_IMM)
src2w = (sljit_s32)(src2w);
- if (GET_FLAGS(op))
+ if (HAS_FLAGS(op))
flags |= ALT_SIGN_EXT;
}
#endif
- if (op & SLJIT_SET_O)
+ if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
- if (src2 == TMP_REG2)
- flags |= ALT_KEEP_CACHE;
switch (GET_OPCODE(op)) {
case SLJIT_ADD:
- if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
+ if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
if (TEST_SL_IMM(src2, src2w)) {
compiler->imm = src2w & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
}
if (TEST_SL_IMM(src1, src1w)) {
compiler->imm = src1w & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
}
if (TEST_SH_IMM(src2, src2w)) {
compiler->imm = (src2w >> 16) & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
}
if (TEST_SH_IMM(src1, src1w)) {
compiler->imm = (src1w >> 16) & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
}
/* Range between -1 and -32768 is covered above. */
if (TEST_ADD_IMM(src2, src2w)) {
compiler->imm = src2w & 0xffffffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
}
if (TEST_ADD_IMM(src1, src1w)) {
compiler->imm = src1w & 0xffffffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
}
}
- if (!(GET_FLAGS(op) & (SLJIT_SET_E | SLJIT_SET_O))) {
+ if (HAS_FLAGS(op)) {
if (TEST_SL_IMM(src2, src2w)) {
compiler->imm = src2w & 0xffff;
return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
@@ -1540,75 +1315,75 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
}
}
- return emit_op(compiler, SLJIT_ADD, flags, dst, dstw, src1, src1w, src2, src2w);
+ return emit_op(compiler, SLJIT_ADD, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM4 : 0), dst, dstw, src1, src1w, src2, src2w);
case SLJIT_ADDC:
- return emit_op(compiler, SLJIT_ADDC, flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w);
+ return emit_op(compiler, SLJIT_ADDC, flags, dst, dstw, src1, src1w, src2, src2w);
case SLJIT_SUB:
- if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
+ if (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_LESS_EQUAL) {
+ if (dst == SLJIT_UNUSED) {
+ if (TEST_UL_IMM(src2, src2w)) {
+ compiler->imm = src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= (SIMM_MAX + 1)) {
+ compiler->imm = src2w;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
if (TEST_SL_IMM(src2, -src2w)) {
compiler->imm = (-src2w) & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
}
if (TEST_SL_IMM(src1, src1w)) {
compiler->imm = src1w & 0xffff;
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
}
if (TEST_SH_IMM(src2, -src2w)) {
compiler->imm = ((-src2w) >> 16) & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
}
/* Range between -1 and -32768 is covered above. */
if (TEST_ADD_IMM(src2, -src2w)) {
compiler->imm = -src2w & 0xffffffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
}
}
- if (dst == SLJIT_UNUSED && (op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S)) && !(op & (SLJIT_SET_O | SLJIT_SET_C))) {
- if (!(op & SLJIT_SET_U)) {
- /* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */
- if (TEST_SL_IMM(src2, src2w)) {
- compiler->imm = src2w & 0xffff;
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
- }
- if (GET_FLAGS(op) == SLJIT_SET_E && TEST_SL_IMM(src1, src1w)) {
- compiler->imm = src1w & 0xffff;
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
- }
- }
- if (!(op & (SLJIT_SET_E | SLJIT_SET_S))) {
- /* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */
- if (TEST_UL_IMM(src2, src2w)) {
- compiler->imm = src2w & 0xffff;
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
- }
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
- }
- if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= 0x7fff) {
- compiler->imm = src2w;
- return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+
+ if (dst == SLJIT_UNUSED && GET_FLAG_TYPE(op) != GET_FLAG_TYPE(SLJIT_SET_CARRY)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src1, src1w, TMP_REG2, 0);
}
- return emit_op(compiler, SLJIT_SUB, flags | ((op & SLJIT_SET_U) ? ALT_FORM4 : 0) | ((op & (SLJIT_SET_E | SLJIT_SET_S)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
}
- if (!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O))) {
- if (TEST_SL_IMM(src2, -src2w)) {
- compiler->imm = (-src2w) & 0xffff;
- return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
- }
+
+ if (TEST_SL_IMM(src2, -src2w)) {
+ compiler->imm = (-src2w) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
}
/* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */
- return emit_op(compiler, SLJIT_SUB, flags | (!(op & SLJIT_SET_U) ? 0 : ALT_FORM6), dst, dstw, src1, src1w, src2, src2w);
+ return emit_op(compiler, SLJIT_SUB, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
case SLJIT_SUBC:
- return emit_op(compiler, SLJIT_SUBC, flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w);
+ return emit_op(compiler, SLJIT_SUBC, flags, dst, dstw, src1, src1w, src2, src2w);
case SLJIT_MUL:
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
if (op & SLJIT_I32_OP)
flags |= ALT_FORM2;
#endif
- if (!GET_FLAGS(op)) {
+ if (!HAS_FLAGS(op)) {
if (TEST_SL_IMM(src2, src2w)) {
compiler->imm = src2w & 0xffff;
return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
@@ -1618,13 +1393,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
}
}
+ else
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
return emit_op(compiler, SLJIT_MUL, flags, dst, dstw, src1, src1w, src2, src2w);
case SLJIT_AND:
case SLJIT_OR:
case SLJIT_XOR:
/* Commutative unsigned operations. */
- if (!GET_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) {
+ if (!HAS_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) {
if (TEST_UL_IMM(src2, src2w)) {
compiler->imm = src2w;
return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
@@ -1642,7 +1419,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
}
}
- if (!GET_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) {
+ if (GET_OPCODE(op) != SLJIT_AND && GET_OPCODE(op) != SLJIT_AND) {
+ /* Unlike or and xor, and resets unwanted bits as well. */
if (TEST_UI_IMM(src2, src2w)) {
compiler->imm = src2w;
return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
@@ -1654,12 +1432,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
}
return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w);
- case SLJIT_ASHR:
- if (op & SLJIT_KEEP_FLAGS)
- flags |= ALT_FORM3;
- /* Fall through. */
case SLJIT_SHL:
case SLJIT_LSHR:
+ case SLJIT_ASHR:
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
if (op & SLJIT_I32_OP)
flags |= ALT_FORM2;
@@ -1683,7 +1458,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg;
+ return freg_map[reg];
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -1699,16 +1474,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#else
- /* Available by default. */
- return 1;
-#endif
-}
-
#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 6))
#define SELECT_FOP(op, single, double) ((op & SLJIT_F32_OP) ? single : double)
@@ -1733,7 +1498,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
{
if (src & SLJIT_MEM) {
/* We can ignore the temporary data store on the stack from caching point of view. */
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
src = TMP_FREG1;
}
@@ -1741,28 +1506,21 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
op = GET_OPCODE(op);
FAIL_IF(push_inst(compiler, (op == SLJIT_CONV_S32_FROM_F64 ? FCTIWZ : FCTIDZ) | FD(TMP_FREG1) | FB(src)));
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (op == SLJIT_CONV_SW_FROM_F64) {
if (FAST_IS_REG(dst)) {
- FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0));
- return emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0);
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ return emit_op_mem(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
}
- return emit_op_mem2(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, 0, 0);
+ return emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, TMP_REG1);
}
-
#else
FAIL_IF(push_inst(compiler, FCTIWZ | FD(TMP_FREG1) | FB(src)));
-
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
#endif
if (FAST_IS_REG(dst)) {
FAIL_IF(load_immediate(compiler, TMP_REG1, FLOAT_TMP_MEM_OFFSET));
FAIL_IF(push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(SLJIT_SP) | B(TMP_REG1)));
- return emit_op_mem2(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0);
+ return emit_op_mem(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
}
SLJIT_ASSERT(dst & SLJIT_MEM);
@@ -1813,21 +1571,21 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, EXTSW | S(src) | A(TMP_REG1)));
else
- FAIL_IF(emit_op_mem2(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
+ FAIL_IF(emit_op_mem(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
src = TMP_REG1;
}
if (FAST_IS_REG(src)) {
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
- FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
}
else
- FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
if (dst & SLJIT_MEM)
- return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
if (op & SLJIT_F32_OP)
return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
return SLJIT_SUCCESS;
@@ -1843,7 +1601,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
invert_sign = 0;
}
else if (!FAST_IS_REG(src)) {
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
src = TMP_REG1;
}
@@ -1855,17 +1613,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330));
if (invert_sign)
FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000));
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(0) | 0x8000));
- FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW));
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
- FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2)));
if (dst & SLJIT_MEM)
- return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
if (op & SLJIT_F32_OP)
return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
return SLJIT_SUCCESS;
@@ -1878,12 +1636,12 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
sljit_s32 src2, sljit_sw src2w)
{
if (src1 & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
src1 = TMP_FREG1;
}
if (src2 & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
src2 = TMP_FREG2;
}
@@ -1897,8 +1655,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
sljit_s32 dst_r;
CHECK_ERROR();
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100) && !(DOUBLE_DATA & 0x4), float_transfer_bit_error);
SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
@@ -1909,7 +1665,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src & SLJIT_MEM) {
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, TMP_REG1));
src = dst_r;
}
@@ -1938,7 +1694,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
}
if (dst & SLJIT_MEM)
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0));
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), dst_r, dst, dstw, TMP_REG1));
return SLJIT_SUCCESS;
}
@@ -1947,7 +1703,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- sljit_s32 dst_r, flags = 0;
+ sljit_s32 dst_r;
CHECK_ERROR();
CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
@@ -1955,46 +1711,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
-
dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
if (src1 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
- FAIL_IF(compiler->error);
- src1 = TMP_FREG1;
- } else
- flags |= ALT_FORM1;
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
+ src1 = TMP_FREG1;
}
if (src2 & SLJIT_MEM) {
- if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
- FAIL_IF(compiler->error);
- src2 = TMP_FREG2;
- } else
- flags |= ALT_FORM2;
- }
-
- if ((flags & (ALT_FORM1 | ALT_FORM2)) == (ALT_FORM1 | ALT_FORM2)) {
- if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w));
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
- }
- else {
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
- }
- }
- else if (flags & ALT_FORM1)
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
- else if (flags & ALT_FORM2)
- FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
-
- if (flags & ALT_FORM1)
- src1 = TMP_FREG1;
- if (flags & ALT_FORM2)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
src2 = TMP_FREG2;
+ }
switch (GET_OPCODE(op)) {
case SLJIT_ADD_F64:
@@ -2014,13 +1741,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
break;
}
- if (dst_r == TMP_FREG2)
- FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0));
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, TMP_REG1));
return SLJIT_SUCCESS;
}
-#undef FLOAT_DATA
#undef SELECT_FOP
/* --------------------------------------------------------------------- */
@@ -2033,10 +1759,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst(compiler, MFLR | D(dst));
@@ -2054,12 +1776,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, MTLR | S(src)));
else {
- if (src & SLJIT_MEM)
- FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw));
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, TMP_REG2, srcw));
+ FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw));
FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2)));
}
+
return push_inst(compiler, BLR);
}
@@ -2093,33 +1813,33 @@ static sljit_ins get_bo_bi_flags(sljit_s32 type)
return (4 << 21) | (2 << 16);
case SLJIT_LESS:
- case SLJIT_LESS_F64:
- return (12 << 21) | ((4 + 0) << 16);
-
- case SLJIT_GREATER_EQUAL:
- case SLJIT_GREATER_EQUAL_F64:
- return (4 << 21) | ((4 + 0) << 16);
-
- case SLJIT_GREATER:
- case SLJIT_GREATER_F64:
- return (12 << 21) | ((4 + 1) << 16);
-
- case SLJIT_LESS_EQUAL:
- case SLJIT_LESS_EQUAL_F64:
- return (4 << 21) | ((4 + 1) << 16);
-
case SLJIT_SIG_LESS:
return (12 << 21) | (0 << 16);
+ case SLJIT_GREATER_EQUAL:
case SLJIT_SIG_GREATER_EQUAL:
return (4 << 21) | (0 << 16);
+ case SLJIT_GREATER:
case SLJIT_SIG_GREATER:
return (12 << 21) | (1 << 16);
+ case SLJIT_LESS_EQUAL:
case SLJIT_SIG_LESS_EQUAL:
return (4 << 21) | (1 << 16);
+ case SLJIT_LESS_F64:
+ return (12 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_GREATER_EQUAL_F64:
+ return (4 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_GREATER_F64:
+ return (12 << 21) | ((4 + 1) << 16);
+
+ case SLJIT_LESS_EQUAL_F64:
+ return (4 << 21) | ((4 + 1) << 16);
+
case SLJIT_OVERFLOW:
case SLJIT_MUL_OVERFLOW:
return (12 << 21) | (3 << 16);
@@ -2141,7 +1861,7 @@ static sljit_ins get_bo_bi_flags(sljit_s32 type)
return (4 << 21) | ((4 + 3) << 16);
default:
- SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3);
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL);
return (20 << 21);
}
}
@@ -2167,7 +1887,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
if (type < SLJIT_JUMP)
jump->flags |= IS_COND;
#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
- if (type >= SLJIT_CALL0)
+ if (type >= SLJIT_CALL)
jump->flags |= IS_CALL;
#endif
@@ -2178,6 +1898,24 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
return jump;
}
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
{
struct sljit_jump *jump = NULL;
@@ -2189,7 +1927,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
if (FAST_IS_REG(src)) {
#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
- if (type >= SLJIT_CALL0) {
+ if (type >= SLJIT_CALL) {
FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
src_r = TMP_CALL_REG;
}
@@ -2199,12 +1937,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
src_r = src;
#endif
} else if (src & SLJIT_IMM) {
+ /* These jumps are converted to jump/call instructions when possible. */
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF(!jump);
set_jump(jump, compiler, JUMP_ADDR);
jump->u.target = srcw;
#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
- if (type >= SLJIT_CALL0)
+ if (type >= SLJIT_CALL)
jump->flags |= IS_CALL;
#endif
FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0));
@@ -2221,153 +1960,302 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
return push_inst(compiler, BCCTR | (20 << 21) | (type >= SLJIT_FAST_CALL ? 1 : 0));
}
-/* Get a bit from CR, all other bits are zeroed. */
-#define GET_CR_BIT(bit, dst) \
- FAIL_IF(push_inst(compiler, MFCR | D(dst))); \
- FAIL_IF(push_inst(compiler, RLWINM | S(dst) | A(dst) | ((1 + (bit)) << 11) | (31 << 6) | (31 << 1)));
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_CALL_REG, 0, TMP_REG1, 0, src, srcw));
+ src = TMP_CALL_REG;
+ }
+
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
-#define INVERT_BIT(dst) \
- FAIL_IF(push_inst(compiler, XORI | S(dst) | A(dst) | 0x1));
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
- sljit_s32 reg, input_flags;
- sljit_s32 flags = GET_ALL_FLAGS(op);
- sljit_sw original_dstw = dstw;
+ sljit_s32 reg, input_flags, cr_bit, invert;
+ sljit_s32 saved_op = op;
+ sljit_sw saved_dstw = dstw;
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
- op = GET_OPCODE(op);
- reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
-
- compiler->cache_arg = 0;
- compiler->cache_argw = 0;
- if (op >= SLJIT_ADD && (src & SLJIT_MEM)) {
- ADJUST_LOCAL_OFFSET(src, srcw);
#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- input_flags = (flags & SLJIT_I32_OP) ? INT_DATA : WORD_DATA;
+ input_flags = (op & SLJIT_I32_OP) ? INT_DATA : WORD_DATA;
#else
- input_flags = WORD_DATA;
+ input_flags = WORD_DATA;
#endif
- FAIL_IF(emit_op_mem2(compiler, input_flags | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
- src = TMP_REG1;
- srcw = 0;
- }
- switch (type & 0xff) {
- case SLJIT_EQUAL:
- GET_CR_BIT(2, reg);
- break;
+ op = GET_OPCODE(op);
+ reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
- case SLJIT_NOT_EQUAL:
- GET_CR_BIT(2, reg);
- INVERT_BIT(reg);
- break;
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG1));
+
+ invert = 0;
+ cr_bit = 0;
+ switch (type & 0xff) {
case SLJIT_LESS:
- case SLJIT_LESS_F64:
- GET_CR_BIT(4 + 0, reg);
+ case SLJIT_SIG_LESS:
break;
case SLJIT_GREATER_EQUAL:
- case SLJIT_GREATER_EQUAL_F64:
- GET_CR_BIT(4 + 0, reg);
- INVERT_BIT(reg);
+ case SLJIT_SIG_GREATER_EQUAL:
+ invert = 1;
break;
case SLJIT_GREATER:
- case SLJIT_GREATER_F64:
- GET_CR_BIT(4 + 1, reg);
+ case SLJIT_SIG_GREATER:
+ cr_bit = 1;
break;
case SLJIT_LESS_EQUAL:
- case SLJIT_LESS_EQUAL_F64:
- GET_CR_BIT(4 + 1, reg);
- INVERT_BIT(reg);
- break;
-
- case SLJIT_SIG_LESS:
- GET_CR_BIT(0, reg);
- break;
-
- case SLJIT_SIG_GREATER_EQUAL:
- GET_CR_BIT(0, reg);
- INVERT_BIT(reg);
+ case SLJIT_SIG_LESS_EQUAL:
+ cr_bit = 1;
+ invert = 1;
break;
- case SLJIT_SIG_GREATER:
- GET_CR_BIT(1, reg);
+ case SLJIT_EQUAL:
+ cr_bit = 2;
break;
- case SLJIT_SIG_LESS_EQUAL:
- GET_CR_BIT(1, reg);
- INVERT_BIT(reg);
+ case SLJIT_NOT_EQUAL:
+ cr_bit = 2;
+ invert = 1;
break;
case SLJIT_OVERFLOW:
case SLJIT_MUL_OVERFLOW:
- GET_CR_BIT(3, reg);
+ cr_bit = 3;
break;
case SLJIT_NOT_OVERFLOW:
case SLJIT_MUL_NOT_OVERFLOW:
- GET_CR_BIT(3, reg);
- INVERT_BIT(reg);
+ cr_bit = 3;
+ invert = 1;
+ break;
+
+ case SLJIT_LESS_F64:
+ cr_bit = 4 + 0;
+ break;
+
+ case SLJIT_GREATER_EQUAL_F64:
+ cr_bit = 4 + 0;
+ invert = 1;
+ break;
+
+ case SLJIT_GREATER_F64:
+ cr_bit = 4 + 1;
+ break;
+
+ case SLJIT_LESS_EQUAL_F64:
+ cr_bit = 4 + 1;
+ invert = 1;
break;
case SLJIT_EQUAL_F64:
- GET_CR_BIT(4 + 2, reg);
+ cr_bit = 4 + 2;
break;
case SLJIT_NOT_EQUAL_F64:
- GET_CR_BIT(4 + 2, reg);
- INVERT_BIT(reg);
+ cr_bit = 4 + 2;
+ invert = 1;
break;
case SLJIT_UNORDERED_F64:
- GET_CR_BIT(4 + 3, reg);
+ cr_bit = 4 + 3;
break;
case SLJIT_ORDERED_F64:
- GET_CR_BIT(4 + 3, reg);
- INVERT_BIT(reg);
+ cr_bit = 4 + 3;
+ invert = 1;
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
+ FAIL_IF(push_inst(compiler, MFCR | D(reg)));
+ FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | ((1 + (cr_bit)) << 11) | (31 << 6) | (31 << 1)));
+
+ if (invert)
+ FAIL_IF(push_inst(compiler, XORI | S(reg) | A(reg) | 0x1));
+
if (op < SLJIT_ADD) {
-#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
- if (op == SLJIT_MOV)
- input_flags = WORD_DATA;
- else {
- op = SLJIT_MOV_U32;
- input_flags = INT_DATA;
- }
-#else
- op = SLJIT_MOV;
- input_flags = WORD_DATA;
-#endif
- if (reg != TMP_REG2)
+ if (!(dst & SLJIT_MEM))
return SLJIT_SUCCESS;
- return emit_op(compiler, op, input_flags, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return emit_op_mem(compiler, input_flags, reg, dst, dstw, TMP_REG1);
}
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- return sljit_emit_op2(compiler, op | flags, dst, original_dstw, src, srcw, TMP_REG2, 0);
+ if (dst & SLJIT_MEM)
+ return sljit_emit_op2(compiler, saved_op, dst, saved_dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return sljit_emit_op2(compiler, saved_op, dst, 0, dst, 0, TMP_REG2, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+#endif
+ mem_flags = WORD_DATA;
+ break;
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ case SLJIT_MOV_U32:
+ mem_flags = INT_DATA;
+ break;
+
+ case SLJIT_MOV_S32:
+ mem_flags = INT_DATA;
+
+ if (!(type & SLJIT_MEM_STORE) && !(type & SLJIT_I32_OP)) {
+ if (mem & OFFS_REG_MASK)
+ mem_flags |= SIGNED_DATA;
+ else
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ break;
+#endif
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ mem_flags = BYTE_DATA;
+ break;
+
+ case SLJIT_MOV_U16:
+ mem_flags = HALF_DATA;
+ break;
+
+ case SLJIT_MOV_S16:
+ mem_flags = HALF_DATA | SIGNED_DATA;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ mem_flags = WORD_DATA;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | B(OFFS_REG(mem))));
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ inst = updated_data_transfer_insts[mem_flags];
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((inst & INT_ALIGNED) && (memw & 0x3) != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+#endif
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | IMM(memw)));
+ }
+
+ if ((mem_flags & LOAD_DATA) && (type & 0xff) == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(reg) | A(reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ mem_flags = FLOAT_DATA(type);
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | B(OFFS_REG(mem)));
+ }
+
+ inst = updated_data_transfer_insts[mem_flags];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | IMM(memw));
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
@@ -2383,7 +2271,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
PTR_FAIL_IF(!const_);
set_const(const_, compiler);
- reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG2;
PTR_FAIL_IF(emit_const(compiler, reg, init_value));
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c
index 4a206f11d0..0671b130cc 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -60,7 +60,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return push_inst(compiler, SRA | D(dst) | S1(dst) | IMM(24), DR(dst));
}
else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
case SLJIT_MOV_U16:
@@ -71,7 +71,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return push_inst(compiler, (op == SLJIT_MOV_S16 ? SRA : SRL) | D(dst) | S1(dst) | IMM(16), DR(dst));
}
else if (dst != src2)
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
case SLJIT_NOT:
@@ -80,18 +80,17 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
case SLJIT_CLZ:
SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
- /* sparc 32 does not support SLJIT_KEEP_FLAGS. Not sure I can fix this. */
FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(src2) | S2(0), SET_FLAGS));
FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2(src2), DR(TMP_REG1)));
FAIL_IF(push_inst(compiler, BICC | DA(0x1) | (7 & DISP_MASK), UNMOVABLE_INS));
- FAIL_IF(push_inst(compiler, OR | (flags & SET_FLAGS) | D(dst) | S1(0) | IMM(32), UNMOVABLE_INS | (flags & SET_FLAGS)));
+ FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(32), UNMOVABLE_INS));
FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(-1), DR(dst)));
/* Loop. */
FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(TMP_REG1) | S2(0), SET_FLAGS));
FAIL_IF(push_inst(compiler, SLL | D(TMP_REG1) | S1(TMP_REG1) | IMM(1), DR(TMP_REG1)));
FAIL_IF(push_inst(compiler, BICC | DA(0xe) | (-2 & DISP_MASK), UNMOVABLE_INS));
- return push_inst(compiler, ADD | (flags & SET_FLAGS) | D(dst) | S1(dst) | IMM(1), UNMOVABLE_INS | (flags & SET_FLAGS));
+ return push_inst(compiler, ADD | D(dst) | S1(dst) | IMM(1), UNMOVABLE_INS);
case SLJIT_ADD:
return push_inst(compiler, ADD | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst) | (flags & SET_FLAGS));
@@ -135,7 +134,126 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return !(flags & SET_FLAGS) ? SLJIT_SUCCESS : push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(dst) | S2(0), SET_FLAGS);
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 reg_index = 8;
+ sljit_s32 word_reg_index = 8;
+ sljit_s32 float_arg_index = 1;
+ sljit_s32 double_arg_count = 0;
+ sljit_s32 float_offset = (16 + 6) * sizeof(sljit_sw);
+ sljit_s32 types = 0;
+ sljit_s32 reg = 0;
+ sljit_s32 move_to_tmp2 = 0;
+
+ if (src)
+ reg = reg_map[*src & REG_MASK];
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_index++;
+ if (reg_index == reg)
+ move_to_tmp2 = 1;
+ reg_index++;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_index++;
+ double_arg_count++;
+ if (reg_index == reg || reg_index + 1 == reg)
+ move_to_tmp2 = 1;
+ reg_index += 2;
+ break;
+ default:
+ if (reg_index != word_reg_index && reg_index < 14 && reg_index == reg)
+ move_to_tmp2 = 1;
+ reg_index++;
+ word_reg_index++;
+ break;
+ }
+
+ if (move_to_tmp2) {
+ move_to_tmp2 = 0;
+ if (reg < 14)
+ FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2A(reg), DR(TMP_REG1)));
+ *src = TMP_REG1;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ arg_types = types;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_index--;
+ FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS));
+ float_offset -= sizeof(sljit_f64);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_index--;
+ if (float_arg_index == 4 && double_arg_count == 4) {
+ FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM((16 + 7) * sizeof(sljit_sw)), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | (1 << 25) | S1(SLJIT_SP) | IMM((16 + 8) * sizeof(sljit_sw)), MOVABLE_INS));
+ }
+ else
+ FAIL_IF(push_inst(compiler, STDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS));
+ float_offset -= sizeof(sljit_f64);
+ break;
+ default:
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ float_offset = (16 + 6) * sizeof(sljit_sw);
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ reg_index--;
+ if (reg_index < 14)
+ FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index));
+ float_offset -= sizeof(sljit_f64);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ reg_index -= 2;
+ if (reg_index < 14) {
+ if ((reg_index & 0x1) != 0) {
+ FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index));
+ if (reg_index < 13)
+ FAIL_IF(push_inst(compiler, LDUW | DA(reg_index + 1) | S1(SLJIT_SP) | IMM(float_offset + sizeof(sljit_sw)), reg_index + 1));
+ }
+ else
+ FAIL_IF(push_inst(compiler, LDD | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index));
+ }
+ float_offset -= sizeof(sljit_f64);
+ break;
+ default:
+ reg_index--;
+ word_reg_index--;
+
+ if (reg_index != word_reg_index) {
+ if (reg_index < 14)
+ FAIL_IF(push_inst(compiler, OR | DA(reg_index) | S1(0) | S2A(word_reg_index), reg_index));
+ else
+ FAIL_IF(push_inst(compiler, STW | DA(word_reg_index) | S1(SLJIT_SP) | IMM(92), word_reg_index));
+ }
+ break;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
return SLJIT_SUCCESS;
}
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c
index 7445fc4723..669ecd8152 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -90,13 +90,19 @@ static void sparc_cache_flush(sljit_ins *from, sljit_ins *to)
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
+/* This register is modified by calls, which affects the instruction
+ in the delay slot if it is used as a source register. */
#define TMP_LINK (SLJIT_NUMBER_OF_REGISTERS + 5)
-#define TMP_FREG1 (0)
-#define TMP_FREG2 ((SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) << 1)
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
- 0, 8, 9, 10, 13, 29, 28, 27, 23, 22, 21, 20, 19, 18, 17, 16, 26, 25, 24, 14, 1, 11, 12, 15
+ 0, 8, 9, 10, 11, 29, 28, 27, 23, 22, 21, 20, 19, 18, 17, 16, 26, 25, 24, 14, 1, 12, 13, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 2, 4, 6, 8, 10, 12, 14
};
/* --------------------------------------------------------------------- */
@@ -104,10 +110,15 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
/* --------------------------------------------------------------------- */
#define D(d) (reg_map[d] << 25)
+#define FD(d) (freg_map[d] << 25)
+#define FDN(d) ((freg_map[d] | 0x1) << 25)
#define DA(d) ((d) << 25)
#define S1(s1) (reg_map[s1] << 14)
-#define S2(s2) (reg_map[s2])
+#define FS1(s1) (freg_map[s1] << 14)
#define S1A(s1) ((s1) << 14)
+#define S2(s2) (reg_map[s2])
+#define FS2(s2) (freg_map[s2])
+#define FS2N(s2) (freg_map[s2] | 0x1)
#define S2A(s2) (s2)
#define IMM_ARG 0x2000
#define DOP(op) ((op) << 5)
@@ -144,6 +155,8 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define FSUBD (OPC1(0x2) | OPC3(0x34) | DOP(0x46))
#define FSUBS (OPC1(0x2) | OPC3(0x34) | DOP(0x45))
#define JMPL (OPC1(0x2) | OPC3(0x38))
+#define LDD (OPC1(0x3) | OPC3(0x03))
+#define LDUW (OPC1(0x3) | OPC3(0x00))
#define NOP (OPC1(0x0) | OPC2(0x04))
#define OR (OPC1(0x2) | OPC3(0x02))
#define ORN (OPC1(0x2) | OPC3(0x06))
@@ -157,6 +170,9 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
#define SRAX (OPC1(0x2) | OPC3(0x27) | (1 << 12))
#define SRL (OPC1(0x2) | OPC3(0x26))
#define SRLX (OPC1(0x2) | OPC3(0x26) | (1 << 12))
+#define STDF (OPC1(0x3) | OPC3(0x27))
+#define STF (OPC1(0x3) | OPC3(0x24))
+#define STW (OPC1(0x3) | OPC3(0x04))
#define SUB (OPC1(0x2) | OPC3(0x04))
#define SUBC (OPC1(0x2) | OPC3(0x0c))
#define TA (OPC1(0x2) | OPC3(0x3a) | (8 << 25))
@@ -394,6 +410,27 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return code;
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+#if (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+ case SLJIT_HAS_CMOV:
+ return 1;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Entry, exit */
/* --------------------------------------------------------------------- */
@@ -412,18 +449,17 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#define MEM_MASK 0x1f
-#define WRITE_BACK 0x00020
-#define ARG_TEST 0x00040
-#define ALT_KEEP_CACHE 0x00080
-#define CUMULATIVE_OP 0x00100
-#define IMM_OP 0x00200
-#define SRC2_IMM 0x00400
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define IMM_OP 0x00100
+#define SRC2_IMM 0x00200
-#define REG_DEST 0x00800
-#define REG2_SOURCE 0x01000
-#define SLOW_SRC1 0x02000
-#define SLOW_SRC2 0x04000
-#define SLOW_DEST 0x08000
+#define REG_DEST 0x00400
+#define REG2_SOURCE 0x00800
+#define SLOW_SRC1 0x01000
+#define SLOW_SRC2 0x02000
+#define SLOW_DEST 0x04000
/* SET_FLAGS (0x10 << 19) also belong here! */
@@ -434,12 +470,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
#endif
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7;
compiler->local_size = local_size;
@@ -458,12 +494,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7;
return SLJIT_SUCCESS;
@@ -525,18 +561,16 @@ static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flag
{
SLJIT_ASSERT(arg & SLJIT_MEM);
- if (!(flags & WRITE_BACK) || !(arg & REG_MASK)) {
- if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN)
- || ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) {
- /* Works for both absoulte and relative addresses (immediate case). */
- if (SLJIT_UNLIKELY(flags & ARG_TEST))
- return 1;
- FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK]
- | ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg))
- | S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)),
- ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS));
- return -1;
- }
+ if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN)
+ || ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) {
+ /* Works for both absoulte and relative addresses (immediate case). */
+ if (SLJIT_UNLIKELY(flags & ARG_TEST))
+ return 1;
+ FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK]
+ | ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg))
+ | S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)),
+ ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS));
+ return -1;
}
return 0;
}
@@ -578,7 +612,6 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl
base = arg & REG_MASK;
if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
argw &= 0x3;
- SLJIT_ASSERT(argw != 0);
/* Using the cache. */
if (((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) && (argw == compiler->cache_argw))
@@ -618,14 +651,11 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl
}
}
- dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg));
+ dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg));
delay_slot = ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS;
if (!base)
return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(arg2) | IMM(0), delay_slot);
- if (!(flags & WRITE_BACK))
- return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot);
- FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot));
- return push_inst(compiler, ADD | D(base) | S1(base) | S2(arg2), DR(base));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot);
}
static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
@@ -663,18 +693,16 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
compiler->cache_argw = 0;
}
- if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM))
- return SLJIT_SUCCESS;
- }
- else if (FAST_IS_REG(dst)) {
- dst_r = dst;
- flags |= REG_DEST;
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
- sugg_src2_r = dst_r;
+ if (dst != SLJIT_UNUSED) {
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ flags |= REG_DEST;
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P)
+ sugg_src2_r = dst_r;
+ }
+ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw))
+ flags |= SLOW_DEST;
}
- else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw))
- flags |= SLOW_DEST;
if (flags & IMM_OP) {
if ((src2 & SLJIT_IMM) && src2w) {
@@ -720,7 +748,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
if (FAST_IS_REG(src2)) {
src2_r = src2;
flags |= REG2_SOURCE;
- if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32)
+ if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P)
dst_r = src2_r;
}
else if (src2 & SLJIT_IMM) {
@@ -731,7 +759,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3
}
else {
src2_r = 0;
- if ((op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) && (dst & SLJIT_MEM))
+ if ((op >= SLJIT_MOV && op <= SLJIT_MOV_P) && (dst & SLJIT_MEM))
dst_r = 0;
}
}
@@ -823,13 +851,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
CHECK_ERROR();
CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
ADJUST_LOCAL_OFFSET(dst, dstw);
ADJUST_LOCAL_OFFSET(src, srcw);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
+
op = GET_OPCODE(op);
switch (op) {
case SLJIT_MOV:
@@ -854,28 +885,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
case SLJIT_MOV_S16:
return emit_op(compiler, SLJIT_MOV_S16, flags | HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
- case SLJIT_MOVU:
- case SLJIT_MOVU_P:
- return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
- case SLJIT_MOVU_U32:
- return emit_op(compiler, SLJIT_MOV_U32, flags | INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
- case SLJIT_MOVU_S32:
- return emit_op(compiler, SLJIT_MOV_S32, flags | INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);
-
- case SLJIT_MOVU_U8:
- return emit_op(compiler, SLJIT_MOV_U8, flags | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
-
- case SLJIT_MOVU_S8:
- return emit_op(compiler, SLJIT_MOV_S8, flags | BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
-
- case SLJIT_MOVU_U16:
- return emit_op(compiler, SLJIT_MOV_U16, flags | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
-
- case SLJIT_MOVU_S16:
- return emit_op(compiler, SLJIT_MOV_S16, flags | HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
-
case SLJIT_NOT:
case SLJIT_CLZ:
return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
@@ -892,7 +901,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
CHECK_ERROR();
CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
@@ -900,6 +909,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src1, src1w);
ADJUST_LOCAL_OFFSET(src2, src2w);
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
+
op = GET_OPCODE(op);
switch (op) {
case SLJIT_ADD:
@@ -921,7 +933,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
if (src2 & SLJIT_IMM)
src2w &= 0x1f;
#else
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
#endif
return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
}
@@ -938,7 +950,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
- return reg << 1;
+ return freg_map[reg];
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -954,16 +966,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#else
- /* Available by default. */
- return 1;
-#endif
-}
-
#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 7))
#define SELECT_FOP(op, single, double) ((op & SLJIT_F32_OP) ? single : double)
#define FLOAT_TMP_MEM_OFFSET (22 * sizeof(sljit_sw))
@@ -976,13 +978,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
src = TMP_FREG1;
}
- else
- src <<= 1;
-
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | DA(TMP_FREG1) | S2A(src), MOVABLE_INS));
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | FD(TMP_FREG1) | FS2(src), MOVABLE_INS));
if (FAST_IS_REG(dst)) {
FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
@@ -997,7 +994,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src & SLJIT_IMM) {
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
@@ -1016,7 +1013,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp
}
FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | DA(dst_r) | S2A(TMP_FREG1), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | FD(dst_r) | FS2(TMP_FREG1), MOVABLE_INS));
if (dst & SLJIT_MEM)
return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
@@ -1031,17 +1028,13 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
src1 = TMP_FREG1;
}
- else
- src1 <<= 1;
if (src2 & SLJIT_MEM) {
FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
src2 = TMP_FREG2;
}
- else
- src2 <<= 1;
- return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | S1A(src1) | S2A(src2), FCC_IS_SET | MOVABLE_INS);
+ return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | FS1(src1) | FS2(src2), FCC_IS_SET | MOVABLE_INS);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
@@ -1060,39 +1053,37 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
op ^= SLJIT_F32_OP;
- dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
if (src & SLJIT_MEM) {
FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
src = dst_r;
}
- else
- src <<= 1;
switch (GET_OPCODE(op)) {
case SLJIT_MOV_F64:
if (src != dst_r) {
if (dst_r != TMP_FREG1) {
- FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r) | S2A(src), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FMOVS | FD(dst_r) | FS2(src), MOVABLE_INS));
if (!(op & SLJIT_F32_OP))
- FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS));
}
else
dst_r = src;
}
break;
case SLJIT_NEG_F64:
- FAIL_IF(push_inst(compiler, FNEGS | DA(dst_r) | S2A(src), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FNEGS | FD(dst_r) | FS2(src), MOVABLE_INS));
if (dst_r != src && !(op & SLJIT_F32_OP))
- FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS));
break;
case SLJIT_ABS_F64:
- FAIL_IF(push_inst(compiler, FABSS | DA(dst_r) | S2A(src), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FABSS | FD(dst_r) | FS2(src), MOVABLE_INS));
if (dst_r != src && !(op & SLJIT_F32_OP))
- FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS));
break;
case SLJIT_CONV_F64_FROM_F32:
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | DA(dst_r) | S2A(src), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | FD(dst_r) | FS2(src), MOVABLE_INS));
op ^= SLJIT_F32_OP;
break;
}
@@ -1118,7 +1109,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
compiler->cache_arg = 0;
compiler->cache_argw = 0;
- dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG2;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
if (src1 & SLJIT_MEM) {
if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
@@ -1127,8 +1118,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
} else
flags |= SLOW_SRC1;
}
- else
- src1 <<= 1;
if (src2 & SLJIT_MEM) {
if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
@@ -1137,8 +1126,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
} else
flags |= SLOW_SRC2;
}
- else
- src2 <<= 1;
if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
@@ -1162,19 +1149,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil
switch (GET_OPCODE(op)) {
case SLJIT_ADD_F64:
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS));
break;
case SLJIT_SUB_F64:
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS));
break;
case SLJIT_MUL_F64:
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS));
break;
case SLJIT_DIV_F64:
- FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS));
break;
}
@@ -1197,10 +1184,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *
CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
ADJUST_LOCAL_OFFSET(dst, dstw);
- /* For UNUSED dst. Uncommon, but possible. */
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
if (FAST_IS_REG(dst))
return push_inst(compiler, OR | D(dst) | S1(0) | S2(TMP_LINK), DR(dst));
@@ -1216,10 +1199,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
if (FAST_IS_REG(src))
FAIL_IF(push_inst(compiler, OR | D(TMP_LINK) | S1(0) | S2(src), DR(TMP_LINK)));
- else if (src & SLJIT_MEM)
+ else
FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_LINK, src, srcw));
- else if (src & SLJIT_IMM)
- FAIL_IF(load_immediate(compiler, TMP_LINK, srcw));
FAIL_IF(push_inst(compiler, JMPL | D(0) | S1(TMP_LINK) | IMM(8), UNMOVABLE_INS));
return push_inst(compiler, NOP, UNMOVABLE_INS);
@@ -1296,7 +1277,7 @@ static sljit_ins get_cc(sljit_s32 type)
return DA(0xf);
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return DA(0x8);
}
}
@@ -1332,21 +1313,38 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
#else
#error "Implementation required"
#endif
- } else {
+ }
+ else {
if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS)
jump->flags |= IS_MOVABLE;
if (type >= SLJIT_FAST_CALL)
jump->flags |= IS_CALL;
}
- PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0));
- PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG2) | IMM(0), UNMOVABLE_INS));
+ PTR_FAIL_IF(emit_const(compiler, TMP_REG1, 0));
+ PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG1) | IMM(0), UNMOVABLE_INS));
jump->addr = compiler->size;
PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
return jump;
}
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
{
struct sljit_jump *jump = NULL;
@@ -1363,17 +1361,18 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
FAIL_IF(!jump);
set_jump(jump, compiler, JUMP_ADDR);
jump->u.target = srcw;
+
if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS)
jump->flags |= IS_MOVABLE;
if (type >= SLJIT_FAST_CALL)
jump->flags |= IS_CALL;
- FAIL_IF(emit_const(compiler, TMP_REG2, 0));
- src_r = TMP_REG2;
+ FAIL_IF(emit_const(compiler, TMP_REG1, 0));
+ src_r = TMP_REG1;
}
else {
- FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw));
- src_r = TMP_REG2;
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src_r = TMP_REG1;
}
FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(src_r) | IMM(0), UNMOVABLE_INS));
@@ -1382,32 +1381,48 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
return push_inst(compiler, NOP, UNMOVABLE_INS);
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
- sljit_s32 reg, flags = (GET_FLAGS(op) ? SET_FLAGS : 0);
+ sljit_s32 reg, flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
op = GET_OPCODE(op);
reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
compiler->cache_arg = 0;
compiler->cache_argw = 0;
- if (op >= SLJIT_ADD && (src & SLJIT_MEM)) {
- ADJUST_LOCAL_OFFSET(src, srcw);
- FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
- src = TMP_REG1;
- srcw = 0;
- }
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw));
type &= 0xff;
if (type < SLJIT_EQUAL_F64)
@@ -1418,10 +1433,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(1), UNMOVABLE_INS));
FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(0), UNMOVABLE_INS));
- if (op >= SLJIT_ADD)
- return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0);
+ if (op >= SLJIT_ADD) {
+ flags |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE;
+ if (dst & SLJIT_MEM)
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return emit_op(compiler, op, flags, dst, 0, dst, 0, TMP_REG2, 0);
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw);
+#else
+#error "Implementation required"
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
- return (reg == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS;
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
#else
#error "Implementation required"
#endif
@@ -1440,7 +1476,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
PTR_FAIL_IF(!const_);
set_const(const_, compiler);
- reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG2;
PTR_FAIL_IF(emit_const(compiler, reg, init_value));
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeTILEGX-encoder.c b/thirdparty/pcre2/src/sljit/sljitNativeTILEGX-encoder.c
index 719632908c..dd82ebae6a 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeTILEGX-encoder.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeTILEGX-encoder.c
@@ -2,7 +2,7 @@
* Stack-less Just-In-Time compiler
*
* Copyright 2013-2013 Tilera Corporation(jiwang@tilera.com). All rights reserved.
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeTILEGX_64.c b/thirdparty/pcre2/src/sljit/sljitNativeTILEGX_64.c
index 177a65b006..003f43a790 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeTILEGX_64.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeTILEGX_64.c
@@ -2,7 +2,7 @@
* Stack-less Just-In-Time compiler
*
* Copyright 2013-2013 Tilera Corporation(jiwang@tilera.com). All rights reserved.
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -687,7 +687,7 @@ static sljit_s32 update_buffer(struct sljit_compiler *compiler)
inst_buf[0] = inst1;
inst_buf_index = 1;
} else
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
#ifdef TILEGX_JIT_DEBUG
return push_inst_nodebug(compiler, bits);
@@ -727,10 +727,10 @@ static sljit_s32 update_buffer(struct sljit_compiler *compiler)
return push_inst(compiler, bits);
#endif
} else
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
static sljit_s32 flush_buffer(struct sljit_compiler *compiler)
@@ -814,7 +814,7 @@ static sljit_s32 push_3_buffer(struct sljit_compiler *compiler, tilegx_mnemonic
break;
default:
printf("unrecoginzed opc: %s\n", opcode->name);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
inst_buf_index++;
@@ -859,7 +859,7 @@ static sljit_s32 push_2_buffer(struct sljit_compiler *compiler, tilegx_mnemonic
break;
default:
printf("unrecoginzed opc: %s\n", opcode->name);
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
inst_buf_index++;
@@ -1952,7 +1952,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl
return SLJIT_SUCCESS;
}
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return SLJIT_SUCCESS;
}
@@ -2092,9 +2092,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
-
op = GET_OPCODE(op);
if (op == SLJIT_MOV_S32 || op == SLJIT_MOV_U32)
mem_type = INT_DATA | SIGNED_DATA;
@@ -2143,7 +2140,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
dst_ar = sugg_dst_ar;
break;
}
@@ -2186,7 +2183,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
case SLJIT_DIVMOD_SW:
case SLJIT_DIV_UW:
case SLJIT_DIV_SW:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
return SLJIT_SUCCESS;
@@ -2487,19 +2484,14 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump * sljit_emit_jump(struct sljit_compil
return jump;
}
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
- return 0;
-}
-
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw)
{
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w)
{
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
}
SLJIT_API_FUNC_ATTRIBUTE struct sljit_const * sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c
index 00333f6b33..8a83e273a4 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -64,20 +64,40 @@ static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 size;
+ sljit_s32 args, size;
sljit_u8 *inst;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+ args = get_arg_count(arg_types);
compiler->args = args;
- compiler->flags_saved = 0;
- size = 1 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3);
+ /* [esp+0] for saving temporaries and function calls. */
+ compiler->stack_tmp_size = 2 * sizeof(sljit_sw);
+
+#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ if (scratches > 3)
+ compiler->stack_tmp_size = 3 * sizeof(sljit_sw);
+#endif
+
+ compiler->saveds_offset = compiler->stack_tmp_size;
+ if (scratches > 3)
+ compiler->saveds_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * sizeof(sljit_sw);
+
+ compiler->locals_offset = compiler->saveds_offset;
+
+ if (saveds > 3)
+ compiler->locals_offset += (saveds - 3) * sizeof(sljit_sw);
+
+ if (options & SLJIT_F64_ALIGNMENT)
+ compiler->locals_offset = (compiler->locals_offset + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1);
+
+ size = 1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3);
#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
size += (args > 0 ? (args * 2) : 0) + (args > 2 ? 2 : 0);
#else
@@ -94,11 +114,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
*inst++ = MOD_REG | (reg_map[TMP_REG1] << 3) | 0x4 /* esp */;
}
#endif
- if (saveds > 2 || scratches > 7)
+ if (saveds > 2 || scratches > 9)
PUSH_REG(reg_map[SLJIT_S2]);
- if (saveds > 1 || scratches > 8)
+ if (saveds > 1 || scratches > 10)
PUSH_REG(reg_map[SLJIT_S1]);
- if (saveds > 0 || scratches > 9)
+ if (saveds > 0 || scratches > 11)
PUSH_REG(reg_map[SLJIT_S0]);
#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
@@ -134,73 +154,106 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
#endif
- SLJIT_COMPILE_ASSERT(SLJIT_LOCALS_OFFSET >= (2 + 4) * sizeof(sljit_uw), require_at_least_two_words);
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET > 0);
+
#if defined(__APPLE__)
/* Ignore pushed registers and SLJIT_LOCALS_OFFSET when computing the aligned local size. */
- saveds = (2 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw);
+ saveds = (2 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw);
local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds;
#else
- if (options & SLJIT_DOUBLE_ALIGNMENT) {
- local_size = SLJIT_LOCALS_OFFSET + ((local_size + 7) & ~7);
-
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 17);
- FAIL_IF(!inst);
-
- INC_SIZE(17);
- inst[0] = MOV_r_rm;
- inst[1] = MOD_REG | (reg_map[TMP_REG1] << 3) | reg_map[SLJIT_SP];
- inst[2] = GROUP_F7;
- inst[3] = MOD_REG | (0 << 3) | reg_map[SLJIT_SP];
- sljit_unaligned_store_sw(inst + 4, 0x4);
- inst[8] = JNE_i8;
- inst[9] = 6;
- inst[10] = GROUP_BINARY_81;
- inst[11] = MOD_REG | (5 << 3) | reg_map[SLJIT_SP];
- sljit_unaligned_store_sw(inst + 12, 0x4);
- inst[16] = PUSH_r + reg_map[TMP_REG1];
- }
+ if (options & SLJIT_F64_ALIGNMENT)
+ local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1));
else
- local_size = SLJIT_LOCALS_OFFSET + ((local_size + 3) & ~3);
+ local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_sw) - 1) & ~(sizeof(sljit_sw) - 1));
#endif
compiler->local_size = local_size;
+
#ifdef _WIN32
if (local_size > 1024) {
#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
FAIL_IF(emit_do_imm(compiler, MOV_r_i32 + reg_map[SLJIT_R0], local_size));
#else
- local_size -= SLJIT_LOCALS_OFFSET;
+ /* Space for a single argument. This amount is excluded when the stack is allocated below. */
+ local_size -= sizeof(sljit_sw);
FAIL_IF(emit_do_imm(compiler, MOV_r_i32 + reg_map[SLJIT_R0], local_size));
- FAIL_IF(emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32,
- SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, SLJIT_LOCALS_OFFSET));
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, sizeof(sljit_sw)));
#endif
- FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack)));
+ FAIL_IF(sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARG1(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack)));
}
#endif
SLJIT_ASSERT(local_size > 0);
- return emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32,
+
+#if !defined(__APPLE__)
+ if (options & SLJIT_F64_ALIGNMENT) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_SP, 0);
+
+ /* Some space might allocated during sljit_grow_stack() above on WIN32. */
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size + sizeof(sljit_sw)));
+
+#if defined _WIN32 && !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ if (compiler->local_size > 1024)
+ FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ TMP_REG1, 0, TMP_REG1, 0, SLJIT_IMM, sizeof(sljit_sw)));
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 6);
+ FAIL_IF(!inst);
+
+ INC_SIZE(6);
+ inst[0] = GROUP_BINARY_81;
+ inst[1] = MOD_REG | AND | reg_map[SLJIT_SP];
+ sljit_unaligned_store_sw(inst + 2, ~(sizeof(sljit_f64) - 1));
+
+ /* The real local size must be used. */
+ return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), compiler->local_size, TMP_REG1, 0);
+ }
+#endif
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
- compiler->args = args;
+ compiler->args = get_arg_count(arg_types);
+
+ /* [esp+0] for saving temporaries and function calls. */
+ compiler->stack_tmp_size = 2 * sizeof(sljit_sw);
+
+#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ if (scratches > 3)
+ compiler->stack_tmp_size = 3 * sizeof(sljit_sw);
+#endif
+
+ compiler->saveds_offset = compiler->stack_tmp_size;
+ if (scratches > 3)
+ compiler->saveds_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * sizeof(sljit_sw);
+
+ compiler->locals_offset = compiler->saveds_offset;
+
+ if (saveds > 3)
+ compiler->locals_offset += (saveds - 3) * sizeof(sljit_sw);
+
+ if (options & SLJIT_F64_ALIGNMENT)
+ compiler->locals_offset = (compiler->locals_offset + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1);
#if defined(__APPLE__)
- saveds = (2 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw);
+ saveds = (2 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw);
compiler->local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds;
#else
- if (options & SLJIT_DOUBLE_ALIGNMENT)
- compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + 7) & ~7);
+ if (options & SLJIT_F64_ALIGNMENT)
+ compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1));
else
- compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + 3) & ~3);
+ compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_sw) - 1) & ~(sizeof(sljit_sw) - 1));
#endif
return SLJIT_SUCCESS;
}
@@ -214,23 +267,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
CHECK(check_sljit_emit_return(compiler, op, src, srcw));
SLJIT_ASSERT(compiler->args >= 0);
- compiler->flags_saved = 0;
FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));
SLJIT_ASSERT(compiler->local_size > 0);
- FAIL_IF(emit_cum_binary(compiler, ADD_r_rm, ADD_rm_r, ADD, ADD_EAX_i32,
- SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size));
#if !defined(__APPLE__)
- if (compiler->options & SLJIT_DOUBLE_ALIGNMENT) {
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
- FAIL_IF(!inst);
-
- INC_SIZE(3);
- inst[0] = MOV_r_rm;
- inst[1] = (reg_map[SLJIT_SP] << 3) | 0x4 /* SIB */;
- inst[2] = (4 << 3) | reg_map[SLJIT_SP];
- }
+ if (compiler->options & SLJIT_F64_ALIGNMENT)
+ EMIT_MOV(compiler, SLJIT_SP, 0, SLJIT_MEM1(SLJIT_SP), compiler->local_size)
+ else
+ FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size));
+#else
+ FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size));
#endif
size = 2 + (compiler->scratches > 7 ? (compiler->scratches - 7) : 0) +
@@ -247,11 +296,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
INC_SIZE(size);
- if (compiler->saveds > 0 || compiler->scratches > 9)
+ if (compiler->saveds > 0 || compiler->scratches > 11)
POP_REG(reg_map[SLJIT_S0]);
- if (compiler->saveds > 1 || compiler->scratches > 8)
+ if (compiler->saveds > 1 || compiler->scratches > 10)
POP_REG(reg_map[SLJIT_S1]);
- if (compiler->saveds > 2 || compiler->scratches > 7)
+ if (compiler->saveds > 2 || compiler->scratches > 9)
POP_REG(reg_map[SLJIT_S2]);
POP_REG(reg_map[TMP_REG1]);
#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
@@ -366,7 +415,7 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
*inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
- if ((a & SLJIT_IMM) || (a == 0))
+ if (a & SLJIT_IMM)
*buf_ptr = 0;
else if (!(flags & EX86_SSE2_OP1))
*buf_ptr = reg_map[a] << 3;
@@ -438,42 +487,324 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
/* Call / return instructions */
/* --------------------------------------------------------------------- */
-static SLJIT_INLINE sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 type)
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+
+static sljit_s32 c_fast_call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr)
{
- sljit_u8 *inst;
+ sljit_s32 stack_size = 0;
+ sljit_s32 word_arg_count = 0;
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ word_arg_count++;
+ if (word_arg_count > 2)
+ stack_size += sizeof(sljit_sw);
+ break;
+ }
-#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
- inst = (sljit_u8*)ensure_buf(compiler, type >= SLJIT_CALL3 ? 1 + 2 + 1 : 1 + 2);
- FAIL_IF(!inst);
- INC_SIZE(type >= SLJIT_CALL3 ? 2 + 1 : 2);
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (word_arg_count_ptr)
+ *word_arg_count_ptr = word_arg_count;
- if (type >= SLJIT_CALL3)
+ return stack_size;
+}
+
+static sljit_s32 c_fast_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_s32 stack_size, sljit_s32 word_arg_count, sljit_s32 swap_args)
+{
+ sljit_u8 *inst;
+ sljit_s32 float_arg_count;
+
+ if (stack_size == sizeof(sljit_sw) && word_arg_count == 3) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
PUSH_REG(reg_map[SLJIT_R2]);
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (reg_map[SLJIT_R2] << 3) | reg_map[SLJIT_R0];
+ }
+ else if (stack_size > 0) {
+ if (word_arg_count >= 4)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->saveds_offset - sizeof(sljit_sw));
+
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size));
+
+ stack_size = 0;
+ arg_types >>= SLJIT_DEF_SHIFT;
+ word_arg_count = 0;
+ float_arg_count = 0;
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count));
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count));
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ word_arg_count++;
+ if (word_arg_count == 3) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, SLJIT_R2, 0);
+ stack_size += sizeof(sljit_sw);
+ }
+ else if (word_arg_count == 4) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, TMP_REG1, 0);
+ stack_size += sizeof(sljit_sw);
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+ }
+
+ if (word_arg_count > 0) {
+ if (swap_args) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+
+ *inst++ = XCHG_EAX_r | reg_map[SLJIT_R2];
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+
+ *inst++ = MOV_r_rm;
+ *inst++ = MOD_REG | (reg_map[SLJIT_R2] << 3) | reg_map[SLJIT_R0];
+ }
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+static sljit_s32 cdecl_call_get_stack_size(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr)
+{
+ sljit_s32 stack_size = 0;
+ sljit_s32 word_arg_count = 0;
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ word_arg_count++;
+ stack_size += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ if (word_arg_count_ptr)
+ *word_arg_count_ptr = word_arg_count;
+
+ if (stack_size <= compiler->stack_tmp_size)
+ return 0;
+
+#if defined(__APPLE__)
+ return ((stack_size - compiler->stack_tmp_size + 15) & ~15);
#else
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 * (type - SLJIT_CALL0));
+ return stack_size - compiler->stack_tmp_size;
+#endif
+}
+
+static sljit_s32 cdecl_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_s32 stack_size, sljit_s32 word_arg_count)
+{
+ sljit_s32 float_arg_count = 0;
+
+ if (word_arg_count >= 4)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->saveds_offset - sizeof(sljit_sw));
+
+ if (stack_size > 0)
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size));
+
+ stack_size = 0;
+ word_arg_count = 0;
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count));
+ stack_size += sizeof(sljit_f32);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count));
+ stack_size += sizeof(sljit_f64);
+ break;
+ default:
+ word_arg_count++;
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, (word_arg_count >= 4) ? TMP_REG1 : word_arg_count, 0);
+ stack_size += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 post_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_s32 stack_size)
+{
+ sljit_u8 *inst;
+ sljit_s32 single;
+
+ if (stack_size > 0)
+ FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size));
+
+ if ((arg_types & SLJIT_DEF_MASK) < SLJIT_ARG_TYPE_F32)
+ return SLJIT_SUCCESS;
+
+ single = ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
FAIL_IF(!inst);
- INC_SIZE(4 * (type - SLJIT_CALL0));
-
- *inst++ = MOV_rm_r;
- *inst++ = MOD_DISP8 | (reg_map[SLJIT_R0] << 3) | 0x4 /* SIB */;
- *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP];
- *inst++ = 0;
- if (type >= SLJIT_CALL2) {
- *inst++ = MOV_rm_r;
- *inst++ = MOD_DISP8 | (reg_map[SLJIT_R1] << 3) | 0x4 /* SIB */;
- *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP];
- *inst++ = sizeof(sljit_sw);
+ INC_SIZE(3);
+ inst[0] = single ? FSTPS : FSTPD;
+ inst[1] = (0x03 << 3) | 0x04;
+ inst[2] = (0x04 << 3) | reg_map[SLJIT_SP];
+
+ return emit_sse2_load(compiler, single, SLJIT_FR0, SLJIT_MEM1(SLJIT_SP), 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_s32 stack_size = 0;
+ sljit_s32 word_arg_count;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ if ((type & 0xff) == SLJIT_CALL) {
+ stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count);
+ PTR_FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, 0));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(post_call_with_args(compiler, arg_types, 0));
+ return jump;
}
- if (type >= SLJIT_CALL3) {
- *inst++ = MOV_rm_r;
- *inst++ = MOD_DISP8 | (reg_map[SLJIT_R2] << 3) | 0x4 /* SIB */;
- *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP];
- *inst++ = 2 * sizeof(sljit_sw);
+#endif
+
+ stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count);
+ PTR_FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(post_call_with_args(compiler, arg_types, stack_size));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 stack_size = 0;
+ sljit_s32 word_arg_count;
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ sljit_s32 swap_args;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ SLJIT_ASSERT(reg_map[SLJIT_R0] == 0 && reg_map[SLJIT_R2] == 1 && SLJIT_R0 == 1 && SLJIT_R2 == 3);
+
+ if ((type & 0xff) == SLJIT_CALL) {
+ stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count);
+ swap_args = 0;
+
+ if (word_arg_count > 0) {
+ if ((src & REG_MASK) == SLJIT_R2 || OFFS_REG(src) == SLJIT_R2) {
+ swap_args = 1;
+ if (((src & REG_MASK) | 0x2) == SLJIT_R2)
+ src ^= 0x2;
+ if ((OFFS_REG(src) | 0x2) == SLJIT_R2)
+ src ^= TO_OFFS_REG(0x2);
+ }
+ }
+
+ FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, swap_args));
+
+ compiler->saveds_offset += stack_size;
+ compiler->locals_offset += stack_size;
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ compiler->saveds_offset -= stack_size;
+ compiler->locals_offset -= stack_size;
+
+ return post_call_with_args(compiler, arg_types, 0);
}
#endif
- return SLJIT_SUCCESS;
+
+ stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count);
+ FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count));
+
+ compiler->saveds_offset += stack_size;
+ compiler->locals_offset += stack_size;
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ compiler->saveds_offset -= stack_size;
+ compiler->locals_offset -= stack_size;
+
+ return post_call_with_args(compiler, arg_types, stack_size);
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
@@ -524,7 +855,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
INC_SIZE(1 + 1);
PUSH_REG(reg_map[src]);
}
- else if (src & SLJIT_MEM) {
+ else {
inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
FAIL_IF(!inst);
*inst++ = GROUP_FF;
@@ -534,16 +865,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
FAIL_IF(!inst);
INC_SIZE(1);
}
- else {
- /* SLJIT_IMM. */
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 5 + 1);
- FAIL_IF(!inst);
-
- INC_SIZE(5 + 1);
- *inst++ = PUSH_i32;
- sljit_unaligned_store_sw(inst, srcw);
- inst += sizeof(sljit_sw);
- }
RET();
return SLJIT_SUCCESS;
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c
index bc92d45680..635ebd087c 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -41,42 +41,55 @@ static sljit_s32 emit_load_imm64(struct sljit_compiler *compiler, sljit_s32 reg,
static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type)
{
+ int short_addr = !(jump->flags & SLJIT_REWRITABLE_JUMP) && !(jump->flags & JUMP_LABEL) && (jump->u.target <= 0xffffffff);
+
+ /* The relative jump below specialized for this case. */
+ SLJIT_ASSERT(reg_map[TMP_REG2] >= 8);
+
if (type < SLJIT_JUMP) {
/* Invert type. */
*code_ptr++ = get_jump_code(type ^ 0x1) - 0x10;
- *code_ptr++ = 10 + 3;
+ *code_ptr++ = short_addr ? (6 + 3) : (10 + 3);
}
- SLJIT_COMPILE_ASSERT(reg_map[TMP_REG3] == 9, tmp3_is_9_first);
- *code_ptr++ = REX_W | REX_B;
- *code_ptr++ = MOV_r_i32 + 1;
+ *code_ptr++ = short_addr ? REX_B : (REX_W | REX_B);
+ *code_ptr++ = MOV_r_i32 | reg_lmap[TMP_REG2];
jump->addr = (sljit_uw)code_ptr;
if (jump->flags & JUMP_LABEL)
jump->flags |= PATCH_MD;
+ else if (short_addr)
+ sljit_unaligned_store_s32(code_ptr, (sljit_s32)jump->u.target);
else
sljit_unaligned_store_sw(code_ptr, jump->u.target);
- code_ptr += sizeof(sljit_sw);
+ code_ptr += short_addr ? sizeof(sljit_s32) : sizeof(sljit_sw);
+
*code_ptr++ = REX_B;
*code_ptr++ = GROUP_FF;
- *code_ptr++ = (type >= SLJIT_FAST_CALL) ? (MOD_REG | CALL_rm | 1) : (MOD_REG | JMP_rm | 1);
+ *code_ptr++ = MOD_REG | (type >= SLJIT_FAST_CALL ? CALL_rm : JMP_rm) | reg_lmap[TMP_REG2];
return code_ptr;
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
- sljit_s32 i, tmp, size, saved_register_size;
+ sljit_s32 args, i, tmp, size, saved_register_size;
sljit_u8 *inst;
CHECK_ERROR();
- CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
- compiler->flags_saved = 0;
+#ifdef _WIN64
+ /* Two/four register slots for parameters plus space for xmm6 register if needed. */
+ if (fscratches >= 6 || fsaveds >= 1)
+ compiler->locals_offset = 6 * sizeof(sljit_sw);
+ else
+ compiler->locals_offset = ((scratches > 2) ? 4 : 2) * sizeof(sljit_sw);
+#endif
/* Including the return address saved by the call instruction. */
saved_register_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
@@ -102,6 +115,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
PUSH_REG(reg_lmap[i]);
}
+ args = get_arg_count(arg_types);
+
if (args > 0) {
size = args * 3;
inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
@@ -155,7 +170,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
INC_SIZE(4 + (3 + sizeof(sljit_s32)));
*inst++ = REX_W;
*inst++ = GROUP_BINARY_83;
- *inst++ = MOD_REG | SUB | 4;
+ *inst++ = MOD_REG | SUB | reg_map[SLJIT_SP];
/* Allocated size for registers must be divisible by 8. */
SLJIT_ASSERT(!(saved_register_size & 0x7));
/* Aligned to 16 byte. */
@@ -167,7 +182,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
local_size -= 4 * sizeof(sljit_sw);
}
/* Second instruction */
- SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R0] < 8, temporary_reg1_is_loreg);
+ SLJIT_ASSERT(reg_map[SLJIT_R0] < 8);
*inst++ = REX_W;
*inst++ = MOV_rm_i32;
*inst++ = MOD_REG | reg_lmap[SLJIT_R0];
@@ -176,29 +191,30 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack)));
+ FAIL_IF(sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARG1(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack)));
}
#endif
- SLJIT_ASSERT(local_size > 0);
- if (local_size <= 127) {
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
- FAIL_IF(!inst);
- INC_SIZE(4);
- *inst++ = REX_W;
- *inst++ = GROUP_BINARY_83;
- *inst++ = MOD_REG | SUB | 4;
- *inst++ = local_size;
- }
- else {
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 7);
- FAIL_IF(!inst);
- INC_SIZE(7);
- *inst++ = REX_W;
- *inst++ = GROUP_BINARY_81;
- *inst++ = MOD_REG | SUB | 4;
- sljit_unaligned_store_s32(inst, local_size);
- inst += sizeof(sljit_s32);
+ if (local_size > 0) {
+ if (local_size <= 127) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ *inst++ = REX_W;
+ *inst++ = GROUP_BINARY_83;
+ *inst++ = MOD_REG | SUB | reg_map[SLJIT_SP];
+ *inst++ = local_size;
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 7);
+ FAIL_IF(!inst);
+ INC_SIZE(7);
+ *inst++ = REX_W;
+ *inst++ = GROUP_BINARY_81;
+ *inst++ = MOD_REG | SUB | reg_map[SLJIT_SP];
+ sljit_unaligned_store_s32(inst, local_size);
+ inst += sizeof(sljit_s32);
+ }
}
#ifdef _WIN64
@@ -216,14 +232,22 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
- sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
{
sljit_s32 saved_register_size;
CHECK_ERROR();
- CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
- set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+#ifdef _WIN64
+ /* Two/four register slots for parameters plus space for xmm6 register if needed. */
+ if (fscratches >= 6 || fsaveds >= 1)
+ compiler->locals_offset = 6 * sizeof(sljit_sw);
+ else
+ compiler->locals_offset = ((scratches > 2) ? 4 : 2) * sizeof(sljit_sw);
+#endif
/* Including the return address saved by the call instruction. */
saved_register_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
@@ -239,7 +263,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
CHECK_ERROR();
CHECK(check_sljit_emit_return(compiler, op, src, srcw));
- compiler->flags_saved = 0;
FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));
#ifdef _WIN64
@@ -253,24 +276,25 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp
}
#endif
- SLJIT_ASSERT(compiler->local_size > 0);
- if (compiler->local_size <= 127) {
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
- FAIL_IF(!inst);
- INC_SIZE(4);
- *inst++ = REX_W;
- *inst++ = GROUP_BINARY_83;
- *inst++ = MOD_REG | ADD | 4;
- *inst = compiler->local_size;
- }
- else {
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 7);
- FAIL_IF(!inst);
- INC_SIZE(7);
- *inst++ = REX_W;
- *inst++ = GROUP_BINARY_81;
- *inst++ = MOD_REG | ADD | 4;
- sljit_unaligned_store_s32(inst, compiler->local_size);
+ if (compiler->local_size > 0) {
+ if (compiler->local_size <= 127) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ *inst++ = REX_W;
+ *inst++ = GROUP_BINARY_83;
+ *inst++ = MOD_REG | ADD | 4;
+ *inst = compiler->local_size;
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 7);
+ FAIL_IF(!inst);
+ INC_SIZE(7);
+ *inst++ = REX_W;
+ *inst++ = GROUP_BINARY_81;
+ *inst++ = MOD_REG | ADD | 4;
+ sljit_unaligned_store_s32(inst, compiler->local_size);
+ }
}
tmp = compiler->scratches;
@@ -365,13 +389,12 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
if (b & SLJIT_MEM) {
if (!(b & OFFS_REG_MASK)) {
if (NOT_HALFWORD(immb)) {
- if (emit_load_imm64(compiler, TMP_REG3, immb))
- return NULL;
+ PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb));
immb = 0;
if (b & REG_MASK)
- b |= TO_OFFS_REG(TMP_REG3);
+ b |= TO_OFFS_REG(TMP_REG2);
else
- b |= TMP_REG3;
+ b |= TMP_REG2;
}
else if (reg_lmap[b & REG_MASK] == 4)
b |= TO_OFFS_REG(SLJIT_SP);
@@ -400,7 +423,11 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
}
}
}
- else if (!(flags & EX86_SSE2_OP2) && reg_map[b] >= 8)
+ else if (!(flags & EX86_SSE2_OP2)) {
+ if (reg_map[b] >= 8)
+ rex |= REX_B;
+ }
+ else if (freg_map[b] >= 8)
rex |= REX_B;
if (a & SLJIT_IMM) {
@@ -427,7 +454,11 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
else {
SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG);
/* reg_map[SLJIT_PREF_SHIFT_REG] is less than 8. */
- if (!(flags & EX86_SSE2_OP1) && reg_map[a] >= 8)
+ if (!(flags & EX86_SSE2_OP1)) {
+ if (reg_map[a] >= 8)
+ rex |= REX_R;
+ }
+ else if (freg_map[a] >= 8)
rex |= REX_R;
}
@@ -454,12 +485,12 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
*inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
- if ((a & SLJIT_IMM) || (a == 0))
+ if (a & SLJIT_IMM)
*buf_ptr = 0;
else if (!(flags & EX86_SSE2_OP1))
*buf_ptr = reg_lmap[a] << 3;
else
- *buf_ptr = a << 3;
+ *buf_ptr = freg_lmap[a] << 3;
}
else {
if (a & SLJIT_IMM) {
@@ -473,7 +504,7 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
}
if (!(b & SLJIT_MEM))
- *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_lmap[b] : b);
+ *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_lmap[b] : freg_lmap[b]);
else if ((b & REG_MASK) != SLJIT_UNUSED) {
if ((b & OFFS_REG_MASK) == SLJIT_UNUSED || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) {
if (immb != 0 || reg_lmap[b & REG_MASK] == 5) {
@@ -531,42 +562,161 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32
/* Call / return instructions */
/* --------------------------------------------------------------------- */
-static SLJIT_INLINE sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 type)
+#ifndef _WIN64
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr, sljit_sw srcw)
{
- sljit_u8 *inst;
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 word_arg_count = 0;
-#ifndef _WIN64
- SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R0] < 8 && reg_map[SLJIT_R2] < 8, args_registers);
+ SLJIT_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R3] == 1 && reg_map[TMP_REG1] == 2);
- inst = (sljit_u8*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6));
- FAIL_IF(!inst);
- INC_SIZE((type < SLJIT_CALL3) ? 3 : 6);
- if (type >= SLJIT_CALL3) {
- *inst++ = REX_W;
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (0x2 /* rdx */ << 3) | reg_lmap[SLJIT_R2];
+ compiler->mode32 = 0;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_DEF_MASK) < SLJIT_ARG_TYPE_F32)
+ word_arg_count++;
+ arg_types >>= SLJIT_DEF_SHIFT;
}
- *inst++ = REX_W;
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (0x7 /* rdi */ << 3) | reg_lmap[SLJIT_R0];
+
+ if (word_arg_count == 0)
+ return SLJIT_SUCCESS;
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ *src_ptr = TMP_REG2;
+ }
+ else if (src == SLJIT_R2 && word_arg_count >= SLJIT_R2)
+ *src_ptr = TMP_REG1;
+
+ if (word_arg_count >= 3)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R2, 0);
+ return emit_mov(compiler, SLJIT_R2, 0, SLJIT_R0, 0);
+}
+
#else
- SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R0] < 8 && reg_map[SLJIT_R2] < 8, args_registers);
- inst = (sljit_u8*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6));
- FAIL_IF(!inst);
- INC_SIZE((type < SLJIT_CALL3) ? 3 : 6);
- if (type >= SLJIT_CALL3) {
- *inst++ = REX_W | REX_R;
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (0x0 /* r8 */ << 3) | reg_lmap[SLJIT_R2];
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr, sljit_sw srcw)
+{
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 data_trandfer = 0;
+ static sljit_u8 word_arg_regs[5] = { 0, SLJIT_R3, SLJIT_R1, SLJIT_R2, TMP_REG1 };
+
+ SLJIT_ASSERT(reg_map[SLJIT_R3] == 1 && reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R2] == 8 && reg_map[TMP_REG1] == 9);
+
+ compiler->mode32 = 0;
+ arg_types >>= SLJIT_DEF_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK);
+
+ switch (arg_types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ case SLJIT_ARG_TYPE_F64:
+ arg_count++;
+ float_arg_count++;
+
+ if (arg_count != float_arg_count)
+ data_trandfer = 1;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count]) {
+ data_trandfer = 1;
+
+ if (src == word_arg_regs[arg_count]) {
+ EMIT_MOV(compiler, TMP_REG2, 0, src, 0);
+ *src_ptr = TMP_REG2;
+ }
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_DEF_SHIFT;
}
- *inst++ = REX_W;
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (0x1 /* rcx */ << 3) | reg_lmap[SLJIT_R0];
-#endif
+
+ if (!data_trandfer)
+ return SLJIT_SUCCESS;
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ *src_ptr = TMP_REG2;
+ }
+
+ while (types) {
+ switch (types & SLJIT_DEF_MASK) {
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 1, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 0, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count])
+ EMIT_MOV(compiler, word_arg_regs[arg_count], 0, word_arg_count, 0);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ types >>= SLJIT_DEF_SHIFT;
+ }
+
return SLJIT_SUCCESS;
}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL, 0));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ FAIL_IF(call_with_args(compiler, arg_types, &src, srcw));
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->skip_checks = 1;
+#endif
+
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
{
sljit_u8 *inst;
@@ -612,11 +762,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
CHECK(check_sljit_emit_fast_return(compiler, src, srcw));
ADJUST_LOCAL_OFFSET(src, srcw);
- if ((src & SLJIT_IMM) && NOT_HALFWORD(srcw)) {
- FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw));
- src = TMP_REG1;
- }
-
if (FAST_IS_REG(src)) {
if (reg_map[src] < 8) {
inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1);
@@ -634,7 +779,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
PUSH_REG(reg_lmap[src]);
}
}
- else if (src & SLJIT_MEM) {
+ else {
/* REX_W is not necessary (src is not immediate). */
compiler->mode32 = 1;
inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
@@ -646,23 +791,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler
FAIL_IF(!inst);
INC_SIZE(1);
}
- else {
- SLJIT_ASSERT(IS_HALFWORD(srcw));
- /* SLJIT_IMM. */
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 5 + 1);
- FAIL_IF(!inst);
-
- INC_SIZE(5 + 1);
- *inst++ = PUSH_i32;
- sljit_unaligned_store_s32(inst, srcw);
- inst += sizeof(sljit_s32);
- }
RET();
return SLJIT_SUCCESS;
}
-
/* --------------------------------------------------------------------- */
/* Extend input */
/* --------------------------------------------------------------------- */
diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c
index 12a0e272af..ab7b36adb2 100644
--- a/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c
+++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -26,7 +26,11 @@
SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
{
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+ return "x86" SLJIT_CPUINFO " ABI:fastcall";
+#else
return "x86" SLJIT_CPUINFO;
+#endif
}
/*
@@ -35,7 +39,7 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
1 - ECX
2 - EDX
3 - EBX
- 4 - none
+ 4 - ESP
5 - EBP
6 - ESI
7 - EDI
@@ -47,7 +51,7 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
1 - RCX
2 - RDX
3 - RBX
- 4 - none
+ 4 - RSP
5 - RBP
6 - RSI
7 - RDI
@@ -67,12 +71,15 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = {
- 0, 0, 2, 1, 0, 0, 0, 0, 7, 6, 3, 4, 5
+ 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 7, 6, 3, 4, 5
};
#define CHECK_EXTRA_REGS(p, w, do) \
- if (p >= SLJIT_R3 && p <= SLJIT_R6) { \
- w = SLJIT_LOCALS_OFFSET + ((p) - (SLJIT_R3 + 4)) * sizeof(sljit_sw); \
+ if (p >= SLJIT_R3 && p <= SLJIT_S3) { \
+ if (p <= compiler->scratches) \
+ w = compiler->saveds_offset - ((p) - SLJIT_R2) * (sljit_sw)sizeof(sljit_sw); \
+ else \
+ w = compiler->locals_offset + ((p) - SLJIT_S2) * (sljit_sw)sizeof(sljit_sw); \
p = SLJIT_MEM1(SLJIT_SP); \
do; \
}
@@ -82,31 +89,39 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = {
/* Last register + 1. */
#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
-#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
/* Note: r12 & 0x7 == 0b100, which decoded as SIB byte present
Note: avoid to use r12 and r13 for memory addessing
- therefore r12 is better for SAVED_EREG than SAVED_REG. */
+ therefore r12 is better to be a higher saved register. */
#ifndef _WIN64
-/* 1st passed in rdi, 2nd argument passed in rsi, 3rd in rdx. */
-static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
- 0, 0, 6, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 7, 9
+/* Args: rdi(=7), rsi(=6), rdx(=2), rcx(=1), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 9
};
/* low-map. reg_map & 0x7. */
-static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = {
- 0, 0, 6, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 7, 1
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 1
};
#else
-/* 1st passed in rcx, 2nd argument passed in rdx, 3rd in r8. */
-static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
- 0, 0, 2, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 10, 8, 9
+/* Args: rcx(=1), rdx(=2), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 8, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 9, 10
};
/* low-map. reg_map & 0x7. */
-static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = {
- 0, 0, 2, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 2, 0, 1
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 0, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 1, 2
};
#endif
+/* Args: xmm0-xmm3 */
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6
+};
+/* low-map. freg_map & 0x7. */
+static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6
+};
+
#define REX_W 0x48
#define REX_R 0x44
#define REX_X 0x42
@@ -166,7 +181,7 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define CALL_i32 0xe8
#define CALL_rm (/* GROUP_FF */ 2 << 3)
#define CDQ 0x99
-#define CMOVNE_r_rm (/* GROUP_0F */ 0x45)
+#define CMOVE_r_rm (/* GROUP_0F */ 0x44)
#define CMP (/* BINARY */ 7 << 3)
#define CMP_EAX_i32 0x3d
#define CMP_r_rm 0x3b
@@ -176,6 +191,8 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define CVTTSD2SI_r_xm 0x2c
#define DIV (/* GROUP_F7 */ 6 << 3)
#define DIVSD_x_xm 0x5e
+#define FSTPS 0xd9
+#define FSTPD 0xdd
#define INT3 0xcc
#define IDIV (/* GROUP_F7 */ 7 << 3)
#define IMUL (/* GROUP_F7 */ 5 << 3)
@@ -214,6 +231,7 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = {
#define POP_r 0x58
#define POP_rm 0x8f
#define POPF 0x9d
+#define PREFETCH 0x18
#define PUSH_i32 0x68
#define PUSH_r 0x50
#define PUSH_rm (/* GROUP_FF */ 6 << 3)
@@ -459,11 +477,7 @@ static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code
code_ptr += sizeof(sljit_s8);
} else {
jump->flags |= PATCH_MW;
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- code_ptr += sizeof(sljit_sw);
-#else
code_ptr += sizeof(sljit_s32);
-#endif
}
return code_ptr;
@@ -585,18 +599,59 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil
return (void*)(code + executable_offset);
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_has_sse2 == -1)
+ get_cpu_features();
+ return cpu_has_sse2;
+#else /* SLJIT_DETECT_SSE2 */
+ return 1;
+#endif /* SLJIT_DETECT_SSE2 */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ case SLJIT_HAS_VIRTUAL_REGISTERS:
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ if (cpu_has_cmov == -1)
+ get_cpu_features();
+ return cpu_has_cmov;
+
+ case SLJIT_HAS_SSE2:
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_has_sse2 == -1)
+ get_cpu_features();
+ return cpu_has_sse2;
+#else
+ return 1;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
/* --------------------------------------------------------------------- */
/* Operators */
/* --------------------------------------------------------------------- */
+#define BINARY_OPCODE(opcode) (((opcode ## _EAX_i32) << 24) | ((opcode ## _r_rm) << 16) | ((opcode ## _rm_r) << 8) | (opcode))
+
static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler,
- sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm,
+ sljit_u32 op_types,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w);
static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler,
- sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm,
+ sljit_u32 op_types,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w);
@@ -605,57 +660,19 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw);
-static SLJIT_INLINE sljit_s32 emit_save_flags(struct sljit_compiler *compiler)
-{
- sljit_u8 *inst;
-
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 5);
- FAIL_IF(!inst);
- INC_SIZE(5);
-#else
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 6);
- FAIL_IF(!inst);
- INC_SIZE(6);
- *inst++ = REX_W;
-#endif
- *inst++ = LEA_r_m; /* lea esp/rsp, [esp/rsp + sizeof(sljit_sw)] */
- *inst++ = 0x64;
- *inst++ = 0x24;
- *inst++ = (sljit_u8)sizeof(sljit_sw);
- *inst++ = PUSHF;
- compiler->flags_saved = 1;
- return SLJIT_SUCCESS;
-}
+#define EMIT_MOV(compiler, dst, dstw, src, srcw) \
+ FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw));
-static SLJIT_INLINE sljit_s32 emit_restore_flags(struct sljit_compiler *compiler, sljit_s32 keep_flags)
-{
- sljit_u8 *inst;
+static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src);
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 5);
- FAIL_IF(!inst);
- INC_SIZE(5);
- *inst++ = POPF;
-#else
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 6);
- FAIL_IF(!inst);
- INC_SIZE(6);
- *inst++ = POPF;
- *inst++ = REX_W;
-#endif
- *inst++ = LEA_r_m; /* lea esp/rsp, [esp/rsp - sizeof(sljit_sw)] */
- *inst++ = 0x64;
- *inst++ = 0x24;
- *inst++ = (sljit_u8)(-(sljit_s8)sizeof(sljit_sw));
- compiler->flags_saved = keep_flags;
- return SLJIT_SUCCESS;
-}
+static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw);
#ifdef _WIN32
#include <malloc.h>
-static void SLJIT_CALL sljit_grow_stack(sljit_sw local_size)
+static void SLJIT_FUNC sljit_grow_stack(sljit_sw local_size)
{
/* Workaround for calling the internal _chkstk() function on Windows.
This function touches all 4k pages belongs to the requested stack space,
@@ -681,15 +698,8 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler,
{
sljit_u8* inst;
- if (dst == SLJIT_UNUSED) {
- /* No destination, doesn't need to setup flags. */
- if (src & SLJIT_MEM) {
- inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw);
- FAIL_IF(!inst);
- *inst = MOV_r_rm;
- }
- return SLJIT_SUCCESS;
- }
+ SLJIT_ASSERT(dst != SLJIT_UNUSED);
+
if (FAST_IS_REG(src)) {
inst = emit_x86_instruction(compiler, 1, src, 0, dst, dstw);
FAIL_IF(!inst);
@@ -711,8 +721,10 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler,
}
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
if (!compiler->mode32 && NOT_HALFWORD(srcw)) {
- FAIL_IF(emit_load_imm64(compiler, TMP_REG2, srcw));
- inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, dst, dstw);
+ /* Immediate to memory move. Only SLJIT_MOV operation copies
+ an immediate directly into memory so TMP_REG1 can be used. */
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
FAIL_IF(!inst);
*inst = MOV_rm_r;
return SLJIT_SUCCESS;
@@ -730,7 +742,8 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler,
return SLJIT_SUCCESS;
}
- /* Memory to memory move. Requires two instruction. */
+ /* Memory to memory move. Only SLJIT_MOV operation copies
+ data from memory to memory so TMP_REG1 can be used. */
inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw);
FAIL_IF(!inst);
*inst = MOV_r_rm;
@@ -740,9 +753,6 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler,
return SLJIT_SUCCESS;
}
-#define EMIT_MOV(compiler, dst, dstw, src, srcw) \
- FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw));
-
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
{
sljit_u8 *inst;
@@ -772,20 +782,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile
case SLJIT_DIVMOD_SW:
case SLJIT_DIV_UW:
case SLJIT_DIV_SW:
- compiler->flags_saved = 0;
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
#ifdef _WIN64
- SLJIT_COMPILE_ASSERT(
+ SLJIT_ASSERT(
reg_map[SLJIT_R0] == 0
&& reg_map[SLJIT_R1] == 2
- && reg_map[TMP_REG1] > 7,
- invalid_register_assignment_for_div_mul);
+ && reg_map[TMP_REG1] > 7);
#else
- SLJIT_COMPILE_ASSERT(
+ SLJIT_ASSERT(
reg_map[SLJIT_R0] == 0
&& reg_map[SLJIT_R1] < 7
- && reg_map[TMP_REG1] == 2,
- invalid_register_assignment_for_div_mul);
+ && reg_map[TMP_REG1] == 2);
#endif
compiler->mode32 = op & SLJIT_I32_OP;
#endif
@@ -909,9 +916,6 @@ static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign,
compiler->mode32 = 0;
#endif
- if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM))
- return SLJIT_SUCCESS; /* Empty instruction. */
-
if (src & SLJIT_IMM) {
if (FAST_IS_REG(dst)) {
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
@@ -1040,6 +1044,30 @@ static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign,
return SLJIT_SUCCESS;
}
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ inst = emit_x86_instruction(compiler, 2, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst++ = PREFETCH;
+
+ if (op >= SLJIT_MOV_U8 && op <= SLJIT_MOV_S8)
+ *inst |= (3 << 3);
+ else if (op >= SLJIT_MOV_U16 && op <= SLJIT_MOV_S16)
+ *inst |= (2 << 3);
+ else
+ *inst |= (1 << 3);
+
+ return SLJIT_SUCCESS;
+}
+
static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
@@ -1051,9 +1079,6 @@ static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign,
compiler->mode32 = 0;
#endif
- if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM))
- return SLJIT_SUCCESS; /* Empty instruction. */
-
if (src & SLJIT_IMM) {
if (FAST_IS_REG(dst)) {
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
@@ -1097,14 +1122,6 @@ static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode,
{
sljit_u8* inst;
- if (dst == SLJIT_UNUSED) {
- EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
- inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
- FAIL_IF(!inst);
- *inst++ = GROUP_F7;
- *inst |= opcode;
- return SLJIT_SUCCESS;
- }
if (dst == src && dstw == srcw) {
/* Same input and output */
inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
@@ -1113,14 +1130,19 @@ static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode,
*inst |= opcode;
return SLJIT_SUCCESS;
}
+
+ if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED))
+ dst = TMP_REG1;
+
if (FAST_IS_REG(dst)) {
EMIT_MOV(compiler, dst, 0, src, srcw);
- inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
FAIL_IF(!inst);
*inst++ = GROUP_F7;
*inst |= opcode;
return SLJIT_SUCCESS;
}
+
EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
FAIL_IF(!inst);
@@ -1136,20 +1158,12 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler,
{
sljit_u8* inst;
- if (dst == SLJIT_UNUSED) {
- EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
- inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
- FAIL_IF(!inst);
- *inst++ = GROUP_F7;
- *inst |= NOT_rm;
- inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0);
- FAIL_IF(!inst);
- *inst = OR_r_rm;
- return SLJIT_SUCCESS;
- }
+ if (dst == SLJIT_UNUSED)
+ dst = TMP_REG1;
+
if (FAST_IS_REG(dst)) {
EMIT_MOV(compiler, dst, 0, src, srcw);
- inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
FAIL_IF(!inst);
*inst++ = GROUP_F7;
*inst |= NOT_rm;
@@ -1158,6 +1172,7 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler,
*inst = OR_r_rm;
return SLJIT_SUCCESS;
}
+
EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
FAIL_IF(!inst);
@@ -1170,6 +1185,10 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler,
return SLJIT_SUCCESS;
}
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+static const sljit_sw emit_clz_arg = 32 + 31;
+#endif
+
static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
@@ -1178,104 +1197,54 @@ static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags,
sljit_s32 dst_r;
SLJIT_UNUSED_ARG(op_flags);
- if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
- /* Just set the zero flag. */
- EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
- inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
- FAIL_IF(!inst);
- *inst++ = GROUP_F7;
- *inst |= NOT_rm;
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 31, TMP_REG1, 0);
-#else
- inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 63 : 31, TMP_REG1, 0);
-#endif
- FAIL_IF(!inst);
- *inst |= SHR;
- return SLJIT_SUCCESS;
- }
- if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
- EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw);
- src = TMP_REG1;
- srcw = 0;
- }
+ if (cpu_has_cmov == -1)
+ get_cpu_features();
- inst = emit_x86_instruction(compiler, 2, TMP_REG1, 0, src, srcw);
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
FAIL_IF(!inst);
*inst++ = GROUP_0F;
*inst = BSR_r_rm;
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- if (FAST_IS_REG(dst))
- dst_r = dst;
- else {
- /* Find an unused temporary register. */
- if ((dst & REG_MASK) != SLJIT_R0 && (dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R0))
- dst_r = SLJIT_R0;
- else if ((dst & REG_MASK) != SLJIT_R1 && (dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R1))
- dst_r = SLJIT_R1;
+ if (cpu_has_cmov) {
+ if (dst_r != TMP_REG1) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 32 + 31);
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0);
+ }
else
- dst_r = SLJIT_R2;
- EMIT_MOV(compiler, dst, dstw, dst_r, 0);
- }
- EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, 32 + 31);
-#else
- dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
- compiler->mode32 = 0;
- EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 64 + 63 : 32 + 31);
- compiler->mode32 = op_flags & SLJIT_I32_OP;
-#endif
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), (sljit_sw)&emit_clz_arg);
- if (cpu_has_cmov == -1)
- get_cpu_features();
-
- if (cpu_has_cmov) {
- inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0);
FAIL_IF(!inst);
*inst++ = GROUP_0F;
- *inst = CMOVNE_r_rm;
- } else {
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
- FAIL_IF(!inst);
- INC_SIZE(4);
+ *inst = CMOVE_r_rm;
+ }
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, 32 + 31));
- *inst++ = JE_i8;
- *inst++ = 2;
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (reg_map[dst_r] << 3) | reg_map[TMP_REG1];
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0);
#else
- inst = (sljit_u8*)ensure_buf(compiler, 1 + 5);
- FAIL_IF(!inst);
- INC_SIZE(5);
+ if (cpu_has_cmov) {
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? (64 + 63) : (32 + 31));
- *inst++ = JE_i8;
- *inst++ = 3;
- *inst++ = REX_W | (reg_map[dst_r] >= 8 ? REX_R : 0) | (reg_map[TMP_REG1] >= 8 ? REX_B : 0);
- *inst++ = MOV_r_rm;
- *inst++ = MOD_REG | (reg_lmap[dst_r] << 3) | reg_lmap[TMP_REG1];
-#endif
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CMOVE_r_rm;
}
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? (64 + 63) : (32 + 31)));
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0);
-#else
inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 63 : 31, dst_r, 0);
#endif
+
FAIL_IF(!inst);
*(inst + 1) |= XOR;
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- if (dst & SLJIT_MEM) {
- inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw);
- FAIL_IF(!inst);
- *inst = XCHG_r_rm;
- }
-#else
if (dst & SLJIT_MEM)
- EMIT_MOV(compiler, dst, dstw, TMP_REG2, 0);
-#endif
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
return SLJIT_SUCCESS;
}
@@ -1283,14 +1252,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_u8* inst;
- sljit_s32 update = 0;
sljit_s32 op_flags = GET_ALL_FLAGS(op);
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
sljit_s32 dst_is_ereg = 0;
- sljit_s32 src_is_ereg = 0;
-#else
-# define src_is_ereg 0
#endif
CHECK_ERROR();
@@ -1299,40 +1263,42 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
ADJUST_LOCAL_OFFSET(src, srcw);
CHECK_EXTRA_REGS(dst, dstw, dst_is_ereg = 1);
- CHECK_EXTRA_REGS(src, srcw, src_is_ereg = 1);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
compiler->mode32 = op_flags & SLJIT_I32_OP;
#endif
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) {
+ if (op <= SLJIT_MOV_P && (src & SLJIT_MEM))
+ return emit_prefetch(compiler, op, src, srcw);
+ return SLJIT_SUCCESS;
+ }
+
op = GET_OPCODE(op);
- if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) {
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
compiler->mode32 = 0;
#endif
+ if (FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
if (op_flags & SLJIT_I32_OP) {
- if (FAST_IS_REG(src) && src == dst) {
- if (!TYPE_CAST_NEEDED(op))
- return SLJIT_SUCCESS;
- }
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
- if (op == SLJIT_MOV_S32 && (src & SLJIT_MEM))
- op = SLJIT_MOV_U32;
- if (op == SLJIT_MOVU_S32 && (src & SLJIT_MEM))
- op = SLJIT_MOVU_U32;
- if (op == SLJIT_MOV_U32 && (src & SLJIT_IMM))
- op = SLJIT_MOV_S32;
- if (op == SLJIT_MOVU_U32 && (src & SLJIT_IMM))
- op = SLJIT_MOVU_S32;
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
+ }
#endif
}
- SLJIT_COMPILE_ASSERT(SLJIT_MOV + 8 == SLJIT_MOVU, movu_offset);
- if (op >= SLJIT_MOVU) {
- update = 1;
- op -= 8;
- }
-
if (src & SLJIT_IMM) {
switch (op) {
case SLJIT_MOV_U8:
@@ -1362,14 +1328,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
#endif
}
- if (SLJIT_UNLIKELY(update) && (src & SLJIT_MEM) && !src_is_ereg && (src & REG_MASK) && (srcw != 0 || (src & OFFS_REG_MASK) != 0)) {
- inst = emit_x86_instruction(compiler, 1, src & REG_MASK, 0, src, srcw);
- FAIL_IF(!inst);
- *inst = LEA_r_m;
- src &= SLJIT_MEM | 0xf;
- srcw = 0;
- }
-
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
if (SLJIT_UNLIKELY(dst_is_ereg) && (!(op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P) || (src & SLJIT_MEM))) {
SLJIT_ASSERT(dst == SLJIT_MEM1(SLJIT_SP));
@@ -1412,40 +1370,23 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
if (SLJIT_UNLIKELY(dst_is_ereg) && dst == TMP_REG1)
return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), dstw, TMP_REG1, 0);
#endif
-
- if (SLJIT_UNLIKELY(update) && (dst & SLJIT_MEM) && (dst & REG_MASK) && (dstw != 0 || (dst & OFFS_REG_MASK) != 0)) {
- inst = emit_x86_instruction(compiler, 1, dst & REG_MASK, 0, dst, dstw);
- FAIL_IF(!inst);
- *inst = LEA_r_m;
- }
return SLJIT_SUCCESS;
}
- if (SLJIT_UNLIKELY(GET_FLAGS(op_flags)))
- compiler->flags_saved = 0;
-
switch (op) {
case SLJIT_NOT:
- if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_E))
+ if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_Z))
return emit_not_with_flags(compiler, dst, dstw, src, srcw);
return emit_unary(compiler, NOT_rm, dst, dstw, src, srcw);
case SLJIT_NEG:
- if (SLJIT_UNLIKELY(op_flags & SLJIT_KEEP_FLAGS) && !compiler->flags_saved)
- FAIL_IF(emit_save_flags(compiler));
return emit_unary(compiler, NEG_rm, dst, dstw, src, srcw);
case SLJIT_CLZ:
- if (SLJIT_UNLIKELY(op_flags & SLJIT_KEEP_FLAGS) && !compiler->flags_saved)
- FAIL_IF(emit_save_flags(compiler));
return emit_clz(compiler, op_flags, dst, dstw, src, srcw);
}
return SLJIT_SUCCESS;
-
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
-# undef src_is_ereg
-#endif
}
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
@@ -1457,8 +1398,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
*(inst + 1) |= (op_imm); \
} \
else { \
- FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immw)); \
- inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, arg, argw); \
+ FAIL_IF(emit_load_imm64(compiler, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, immw)); \
+ inst = emit_x86_instruction(compiler, 1, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, 0, arg, argw); \
FAIL_IF(!inst); \
*inst = (op_mr); \
}
@@ -1479,12 +1420,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile
#endif
static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler,
- sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm,
+ sljit_u32 op_types,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
sljit_u8* inst;
+ sljit_u8 op_eax_imm = (op_types >> 24);
+ sljit_u8 op_rm = (op_types >> 16) & 0xff;
+ sljit_u8 op_mr = (op_types >> 8) & 0xff;
+ sljit_u8 op_imm = op_types & 0xff;
if (dst == SLJIT_UNUSED) {
EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
@@ -1595,12 +1540,16 @@ static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler,
}
static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler,
- sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm,
+ sljit_u32 op_types,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
sljit_u8* inst;
+ sljit_u8 op_eax_imm = (op_types >> 24);
+ sljit_u8 op_rm = (op_types >> 16) & 0xff;
+ sljit_u8 op_mr = (op_types >> 8) & 0xff;
+ sljit_u8 op_imm = op_types & 0xff;
if (dst == SLJIT_UNUSED) {
EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
@@ -1684,7 +1633,7 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler,
sljit_u8* inst;
sljit_s32 dst_r;
- dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
/* Register destination. */
if (dst_r == src1 && !(src2 & SLJIT_IMM)) {
@@ -1736,9 +1685,9 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler,
sljit_unaligned_store_s32(inst, (sljit_s32)src1w);
}
else {
- EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src1w);
if (dst_r != src2)
EMIT_MOV(compiler, dst_r, 0, src2, src2w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w));
inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
FAIL_IF(!inst);
*inst++ = GROUP_0F;
@@ -1779,9 +1728,9 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler,
sljit_unaligned_store_s32(inst, (sljit_s32)src2w);
}
else {
- EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src2w);
if (dst_r != src1)
EMIT_MOV(compiler, dst_r, 0, src1, src1w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
FAIL_IF(!inst);
*inst++ = GROUP_0F;
@@ -1800,13 +1749,13 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler,
*inst = IMUL_r_rm;
}
- if (dst_r == TMP_REG1)
+ if (dst & SLJIT_MEM)
EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
return SLJIT_SUCCESS;
}
-static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler, sljit_s32 keep_flags,
+static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler,
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
@@ -1815,12 +1764,10 @@ static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler, sljit_s32 keep
sljit_s32 dst_r, done = 0;
/* These cases better be left to handled by normal way. */
- if (!keep_flags) {
- if (dst == src1 && dstw == src1w)
- return SLJIT_ERR_UNSUPPORTED;
- if (dst == src2 && dstw == src2w)
- return SLJIT_ERR_UNSUPPORTED;
- }
+ if (dst == src1 && dstw == src1w)
+ return SLJIT_ERR_UNSUPPORTED;
+ if (dst == src2 && dstw == src2w)
+ return SLJIT_ERR_UNSUPPORTED;
dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
@@ -1932,7 +1879,7 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler,
}
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
- if (src2 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+ if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
#else
if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128)) {
#endif
@@ -1949,8 +1896,8 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler,
*inst = GROUP_F7;
}
else {
- FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
- inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src1, src1w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src2w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src1, src1w);
FAIL_IF(!inst);
*inst = TEST_rm_r;
}
@@ -1978,8 +1925,8 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler,
*inst = GROUP_F7;
}
else {
- FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w));
- inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src2, src2w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src1w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
FAIL_IF(!inst);
*inst = TEST_rm_r;
}
@@ -2080,7 +2027,7 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler,
*inst |= mode;
EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
}
- else if (FAST_IS_REG(dst) && dst != src2 && !ADDRESSING_DEPENDS_ON(src2, dst)) {
+ else if (SLOW_IS_REG(dst) && dst != src2 && !ADDRESSING_DEPENDS_ON(src2, dst)) {
if (src1 != dst)
EMIT_MOV(compiler, dst, 0, src1, src1w);
EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0);
@@ -2091,25 +2038,26 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler,
EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
}
else {
- /* This case is really difficult, since ecx itself may used for
- addressing, and we must ensure to work even in that case. */
+ /* This case is complex since ecx itself may be used for
+ addressing, and this case must be supported as well. */
EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
- EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0);
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0);
#else
- /* [esp+0] contains the flags. */
- EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), sizeof(sljit_sw), SLJIT_PREF_SHIFT_REG, 0);
-#endif
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0);
EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
FAIL_IF(!inst);
*inst |= mode;
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0);
-#else
- EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), sizeof(sljit_sw));
#endif
- EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ if (dst != SLJIT_UNUSED)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
}
return SLJIT_SUCCESS;
@@ -2133,7 +2081,7 @@ static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler,
if (!set_flags)
return emit_mov(compiler, dst, dstw, src1, src1w);
/* OR dst, src, 0 */
- return emit_cum_binary(compiler, OR_r_rm, OR_rm_r, OR, OR_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
dst, dstw, src1, src1w, SLJIT_IMM, 0);
}
@@ -2143,10 +2091,10 @@ static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler,
if (!FAST_IS_REG(dst))
FAIL_IF(emit_cmp_binary(compiler, src1, src1w, SLJIT_IMM, 0));
- FAIL_IF(emit_shift(compiler,mode, dst, dstw, src1, src1w, src2, src2w));
+ FAIL_IF(emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w));
if (FAST_IS_REG(dst))
- return emit_cmp_binary(compiler, dst, dstw, SLJIT_IMM, 0);
+ return emit_cmp_binary(compiler, (dst == SLJIT_UNUSED) ? TMP_REG1 : dst, dstw, SLJIT_IMM, 0);
return SLJIT_SUCCESS;
}
@@ -2168,77 +2116,54 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile
compiler->mode32 = op & SLJIT_I32_OP;
#endif
- if (GET_OPCODE(op) >= SLJIT_MUL) {
- if (SLJIT_UNLIKELY(GET_FLAGS(op)))
- compiler->flags_saved = 0;
- else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved)
- FAIL_IF(emit_save_flags(compiler));
- }
+ if (dst == SLJIT_UNUSED && !HAS_FLAGS(op))
+ return SLJIT_SUCCESS;
switch (GET_OPCODE(op)) {
case SLJIT_ADD:
- if (!GET_FLAGS(op)) {
- if (emit_lea_binary(compiler, op & SLJIT_KEEP_FLAGS, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED)
+ if (!HAS_FLAGS(op)) {
+ if (emit_lea_binary(compiler, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED)
return compiler->error;
}
- else
- compiler->flags_saved = 0;
- if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved)
- FAIL_IF(emit_save_flags(compiler));
- return emit_cum_binary(compiler, ADD_r_rm, ADD_rm_r, ADD, ADD_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADD),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_ADDC:
- if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */
- FAIL_IF(emit_restore_flags(compiler, 1));
- else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS))
- FAIL_IF(emit_save_flags(compiler));
- if (SLJIT_UNLIKELY(GET_FLAGS(op)))
- compiler->flags_saved = 0;
- return emit_cum_binary(compiler, ADC_r_rm, ADC_rm_r, ADC, ADC_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADC),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_SUB:
- if (!GET_FLAGS(op)) {
- if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, op & SLJIT_KEEP_FLAGS, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED)
+ if (!HAS_FLAGS(op)) {
+ if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED)
return compiler->error;
}
- else
- compiler->flags_saved = 0;
- if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved)
- FAIL_IF(emit_save_flags(compiler));
+
if (dst == SLJIT_UNUSED)
return emit_cmp_binary(compiler, src1, src1w, src2, src2w);
- return emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32,
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_SUBC:
- if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */
- FAIL_IF(emit_restore_flags(compiler, 1));
- else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS))
- FAIL_IF(emit_save_flags(compiler));
- if (SLJIT_UNLIKELY(GET_FLAGS(op)))
- compiler->flags_saved = 0;
- return emit_non_cum_binary(compiler, SBB_r_rm, SBB_rm_r, SBB, SBB_EAX_i32,
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SBB),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_MUL:
return emit_mul(compiler, dst, dstw, src1, src1w, src2, src2w);
case SLJIT_AND:
if (dst == SLJIT_UNUSED)
return emit_test_binary(compiler, src1, src1w, src2, src2w);
- return emit_cum_binary(compiler, AND_r_rm, AND_rm_r, AND, AND_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(AND),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_OR:
- return emit_cum_binary(compiler, OR_r_rm, OR_rm_r, OR, OR_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_XOR:
- return emit_cum_binary(compiler, XOR_r_rm, XOR_rm_r, XOR, XOR_EAX_i32,
+ return emit_cum_binary(compiler, BINARY_OPCODE(XOR),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_SHL:
- return emit_shift_with_flags(compiler, SHL, GET_FLAGS(op),
+ return emit_shift_with_flags(compiler, SHL, HAS_FLAGS(op),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_LSHR:
- return emit_shift_with_flags(compiler, SHR, GET_FLAGS(op),
+ return emit_shift_with_flags(compiler, SHR, HAS_FLAGS(op),
dst, dstw, src1, src1w, src2, src2w);
case SLJIT_ASHR:
- return emit_shift_with_flags(compiler, SAR, GET_FLAGS(op),
+ return emit_shift_with_flags(compiler, SAR, HAS_FLAGS(op),
dst, dstw, src1, src1w, src2, src2w);
}
@@ -2249,7 +2174,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_register_index(reg));
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- if (reg >= SLJIT_R3 && reg <= SLJIT_R6)
+ if (reg >= SLJIT_R3 && reg <= SLJIT_R8)
return -1;
#endif
return reg_map[reg];
@@ -2258,7 +2183,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
{
CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
return reg;
+#else
+ return freg_map[reg];
+#endif
}
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
@@ -2280,36 +2209,25 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c
/* Floating point operators */
/* --------------------------------------------------------------------- */
-/* Alignment + 2 * 16 bytes. */
-static sljit_s32 sse2_data[3 + (4 + 4) * 2];
+/* Alignment(3) + 4 * 16 bytes. */
+static sljit_s32 sse2_data[3 + (4 * 4)];
static sljit_s32 *sse2_buffer;
static void init_compiler(void)
{
+ /* Align to 16 bytes. */
sse2_buffer = (sljit_s32*)(((sljit_uw)sse2_data + 15) & ~0xf);
- /* Single precision constants. */
+
+ /* Single precision constants (each constant is 16 byte long). */
sse2_buffer[0] = 0x80000000;
sse2_buffer[4] = 0x7fffffff;
- /* Double precision constants. */
+ /* Double precision constants (each constant is 16 byte long). */
sse2_buffer[8] = 0;
sse2_buffer[9] = 0x80000000;
sse2_buffer[12] = 0xffffffff;
sse2_buffer[13] = 0x7fffffff;
}
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void)
-{
-#ifdef SLJIT_IS_FPU_AVAILABLE
- return SLJIT_IS_FPU_AVAILABLE;
-#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
- if (cpu_has_sse2 == -1)
- get_cpu_features();
- return cpu_has_sse2;
-#else /* SLJIT_DETECT_SSE2 */
- return 1;
-#endif /* SLJIT_DETECT_SSE2 */
-}
-
static sljit_s32 emit_sse2(struct sljit_compiler *compiler, sljit_u8 opcode,
sljit_s32 single, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w)
{
@@ -2350,7 +2268,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
sljit_s32 dst, sljit_sw dstw,
sljit_s32 src, sljit_sw srcw)
{
- sljit_s32 dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
sljit_u8 *inst;
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
@@ -2363,7 +2281,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp
*inst++ = GROUP_0F;
*inst = CVTTSD2SI_r_xm;
- if (dst_r == TMP_REG1 && dst != SLJIT_UNUSED)
+ if (dst & SLJIT_MEM)
return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
return SLJIT_SUCCESS;
}
@@ -2407,11 +2325,11 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile
sljit_s32 src1, sljit_sw src1w,
sljit_s32 src2, sljit_sw src2w)
{
- compiler->flags_saved = 0;
if (!FAST_IS_REG(src1)) {
FAIL_IF(emit_sse2_load(compiler, op & SLJIT_F32_OP, TMP_FREG, src1, src1w));
src1 = TMP_FREG;
}
+
return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_F32_OP), src1, src2, src2w);
}
@@ -2456,7 +2374,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil
return SLJIT_SUCCESS;
}
- if (SLOW_IS_REG(dst)) {
+ if (FAST_IS_REG(dst)) {
dst_r = dst;
if (dst != src)
FAIL_IF(emit_sse2_load(compiler, op & SLJIT_F32_OP, dst_r, src, srcw));
@@ -2554,11 +2472,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi
CHECK_ERROR_PTR();
CHECK_PTR(check_sljit_emit_label(compiler));
- /* We should restore the flags before the label,
- since other taken jumps has their own flags as well. */
- if (SLJIT_UNLIKELY(compiler->flags_saved))
- PTR_FAIL_IF(emit_restore_flags(compiler, 0));
-
if (compiler->last_label && compiler->last_label->size == compiler->size)
return compiler->last_label;
@@ -2583,20 +2496,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile
CHECK_ERROR_PTR();
CHECK_PTR(check_sljit_emit_jump(compiler, type));
- if (SLJIT_UNLIKELY(compiler->flags_saved)) {
- if ((type & 0xff) <= SLJIT_JUMP)
- PTR_FAIL_IF(emit_restore_flags(compiler, 0));
- compiler->flags_saved = 0;
- }
-
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
PTR_FAIL_IF_NULL(jump);
set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
type &= 0xff;
- if (type >= SLJIT_CALL1)
- PTR_FAIL_IF(call_with_args(compiler, type));
-
/* Worst case size. */
#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
compiler->size += (type >= SLJIT_JUMP) ? 5 : 6;
@@ -2623,32 +2527,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
CHECK_EXTRA_REGS(src, srcw, (void)0);
- if (SLJIT_UNLIKELY(compiler->flags_saved)) {
- if (type <= SLJIT_JUMP)
- FAIL_IF(emit_restore_flags(compiler, 0));
- compiler->flags_saved = 0;
- }
-
- if (type >= SLJIT_CALL1) {
-#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
-#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
- if (src == SLJIT_R2) {
- EMIT_MOV(compiler, TMP_REG1, 0, src, 0);
- src = TMP_REG1;
- }
- if (src == SLJIT_MEM1(SLJIT_SP) && type >= SLJIT_CALL3)
- srcw += sizeof(sljit_sw);
-#endif
-#endif
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && defined(_WIN64)
- if (src == SLJIT_R2) {
- EMIT_MOV(compiler, TMP_REG1, 0, src, 0);
- src = TMP_REG1;
- }
-#endif
- FAIL_IF(call_with_args(compiler, type));
- }
-
if (src == SLJIT_IMM) {
jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
FAIL_IF_NULL(jump);
@@ -2683,37 +2561,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
sljit_s32 dst, sljit_sw dstw,
- sljit_s32 src, sljit_sw srcw,
sljit_s32 type)
{
sljit_u8 *inst;
sljit_u8 cond_set = 0;
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
sljit_s32 reg;
-#else
- /* CHECK_EXTRA_REGS migh overwrite these values. */
+#endif
+ /* ADJUST_LOCAL_OFFSET and CHECK_EXTRA_REGS might overwrite these values. */
sljit_s32 dst_save = dst;
sljit_sw dstw_save = dstw;
-#endif
CHECK_ERROR();
- CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
- SLJIT_UNUSED_ARG(srcw);
-
- if (dst == SLJIT_UNUSED)
- return SLJIT_SUCCESS;
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
ADJUST_LOCAL_OFFSET(dst, dstw);
CHECK_EXTRA_REGS(dst, dstw, (void)0);
- if (SLJIT_UNLIKELY(compiler->flags_saved))
- FAIL_IF(emit_restore_flags(compiler, op & SLJIT_KEEP_FLAGS));
type &= 0xff;
/* setcc = jcc + 0x10. */
cond_set = get_jump_code(type) + 0x10;
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
- if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && dst == src) {
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst)) {
inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 3);
FAIL_IF(!inst);
INC_SIZE(4 + 3);
@@ -2728,7 +2598,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
return SLJIT_SUCCESS;
}
- reg = (op == SLJIT_MOV && FAST_IS_REG(dst)) ? dst : TMP_REG1;
+ reg = (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG1;
inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 4);
FAIL_IF(!inst);
@@ -2739,6 +2609,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
*inst++ = cond_set;
*inst++ = MOD_REG | reg_lmap[reg];
*inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : (REX_B | REX_R));
+ /* The movzx instruction does not affect flags. */
*inst++ = GROUP_0F;
*inst++ = MOVZX_r_rm8;
*inst = MOD_REG | (reg_lmap[reg] << 3) | reg_lmap[reg];
@@ -2750,12 +2621,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
compiler->mode32 = GET_OPCODE(op) != SLJIT_MOV;
return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
}
+
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
compiler->skip_checks = 1;
#endif
- return sljit_emit_op2(compiler, op, dst, dstw, dst, dstw, TMP_REG1, 0);
-#else /* SLJIT_CONFIG_X86_64 */
+ return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0);
+
+#else
+ /* The SLJIT_CONFIG_X86_32 code path starts here. */
if (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) {
if (reg_map[dst] <= 4) {
/* Low byte is accessible. */
@@ -2809,8 +2683,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
return SLJIT_SUCCESS;
}
- if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && dst == src && reg_map[dst] <= 4) {
- SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R0] == 0, scratch_reg1_must_be_eax);
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && reg_map[dst] <= 4) {
+ SLJIT_ASSERT(reg_map[SLJIT_R0] == 0);
+
if (dst != SLJIT_R0) {
inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 2 + 1);
FAIL_IF(!inst);
@@ -2869,6 +2744,46 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co
#endif /* SLJIT_CONFIG_X86_64 */
}
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ dst_reg &= ~SLJIT_I32_OP;
+
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV) || (dst_reg >= SLJIT_R3 && dst_reg <= SLJIT_S3))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#else
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#endif
+
+ /* ADJUST_LOCAL_OFFSET is not needed. */
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = dst_reg & SLJIT_I32_OP;
+ dst_reg &= ~SLJIT_I32_OP;
+#endif
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = get_jump_code(type & 0xff) - 0x40;
+ return SLJIT_SUCCESS;
+}
+
SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
{
CHECK_ERROR();
@@ -2887,16 +2802,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c
if (NOT_HALFWORD(offset)) {
FAIL_IF(emit_load_imm64(compiler, TMP_REG1, offset));
#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
- SLJIT_ASSERT(emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED);
+ SLJIT_ASSERT(emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED);
return compiler->error;
#else
- return emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0);
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0);
#endif
}
#endif
if (offset != 0)
- return emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
return emit_mov(compiler, dst, dstw, SLJIT_SP, 0);
}
@@ -2920,14 +2835,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
compiler->mode32 = 0;
- reg = SLOW_IS_REG(dst) ? dst : TMP_REG1;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
if (emit_load_imm64(compiler, reg, init_value))
return NULL;
#else
- if (dst == SLJIT_UNUSED)
- dst = TMP_REG1;
-
if (emit_mov(compiler, dst, dstw, SLJIT_IMM, init_value))
return NULL;
#endif
@@ -2962,69 +2874,3 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta
SLJIT_UNUSED_ARG(executable_offset);
sljit_unaligned_store_sw((void*)addr, new_constant);
}
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_is_sse2_available(void)
-{
-#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
- if (cpu_has_sse2 == -1)
- get_cpu_features();
- return cpu_has_sse2;
-#else
- return 1;
-#endif
-}
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_is_cmov_available(void)
-{
- if (cpu_has_cmov == -1)
- get_cpu_features();
- return cpu_has_cmov;
-}
-
-SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_emit_cmov(struct sljit_compiler *compiler,
- sljit_s32 type,
- sljit_s32 dst_reg,
- sljit_s32 src, sljit_sw srcw)
-{
- sljit_u8* inst;
-
- CHECK_ERROR();
-#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
- CHECK_ARGUMENT(sljit_x86_is_cmov_available());
- CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP)));
- CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64);
- CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_I32_OP));
- FUNCTION_CHECK_SRC(src, srcw);
-#endif
-#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
- if (SLJIT_UNLIKELY(!!compiler->verbose)) {
- fprintf(compiler->verbose, " x86_cmov%s %s%s, ",
- !(dst_reg & SLJIT_I32_OP) ? "" : ".i",
- jump_names[type & 0xff], JUMP_POSTFIX(type));
- sljit_verbose_reg(compiler, dst_reg & ~SLJIT_I32_OP);
- fprintf(compiler->verbose, ", ");
- sljit_verbose_param(compiler, src, srcw);
- fprintf(compiler->verbose, "\n");
- }
-#endif
-
- ADJUST_LOCAL_OFFSET(src, srcw);
- CHECK_EXTRA_REGS(src, srcw, (void)0);
-
-#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
- compiler->mode32 = dst_reg & SLJIT_I32_OP;
-#endif
- dst_reg &= ~SLJIT_I32_OP;
-
- if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
- EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw);
- src = TMP_REG1;
- srcw = 0;
- }
-
- inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw);
- FAIL_IF(!inst);
- *inst++ = GROUP_0F;
- *inst = get_jump_code(type & 0xff) - 0x40;
- return SLJIT_SUCCESS;
-}
diff --git a/thirdparty/pcre2/src/sljit/sljitProtExecAllocator.c b/thirdparty/pcre2/src/sljit/sljitProtExecAllocator.c
new file mode 100644
index 0000000000..8a5b2b3cfe
--- /dev/null
+++ b/thirdparty/pcre2/src/sljit/sljitProtExecAllocator.c
@@ -0,0 +1,421 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ This file contains a simple executable memory allocator
+
+ It is assumed, that executable code blocks are usually medium (or sometimes
+ large) memory blocks, and the allocator is not too frequently called (less
+ optimized than other allocators). Thus, using it as a generic allocator is
+ not suggested.
+
+ How does it work:
+ Memory is allocated in continuous memory areas called chunks by alloc_chunk()
+ Chunk format:
+ [ block ][ block ] ... [ block ][ block terminator ]
+
+ All blocks and the block terminator is started with block_header. The block
+ header contains the size of the previous and the next block. These sizes
+ can also contain special values.
+ Block size:
+ 0 - The block is a free_block, with a different size member.
+ 1 - The block is a block terminator.
+ n - The block is used at the moment, and the value contains its size.
+ Previous block size:
+ 0 - This is the first block of the memory chunk.
+ n - The size of the previous block.
+
+ Using these size values we can go forward or backward on the block chain.
+ The unused blocks are stored in a chain list pointed by free_blocks. This
+ list is useful if we need to find a suitable memory area when the allocator
+ is called.
+
+ When a block is freed, the new free block is connected to its adjacent free
+ blocks if possible.
+
+ [ free block ][ used block ][ free block ]
+ and "used block" is freed, the three blocks are connected together:
+ [ one big free block ]
+*/
+
+/* --------------------------------------------------------------------- */
+/* System (OS) functions */
+/* --------------------------------------------------------------------- */
+
+/* 64 KByte. */
+#define CHUNK_SIZE 0x10000
+
+struct chunk_header {
+ void *executable;
+ int fd;
+};
+
+/*
+ alloc_chunk / free_chunk :
+ * allocate executable system memory chunks
+ * the size is always divisible by CHUNK_SIZE
+ allocator_grab_lock / allocator_release_lock :
+ * make the allocator thread safe
+ * can be empty if the OS (or the application) does not support threading
+ * only the allocator requires this lock, sljit is fully thread safe
+ as it only uses local variables
+*/
+
+#include <fcntl.h>
+
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+#ifdef __O_TMPFILE
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#endif
+#endif
+
+int mkostemp(char *template, int flags);
+char *secure_getenv(const char *name);
+
+static SLJIT_INLINE int create_tempfile(void)
+{
+ int fd;
+
+ char tmp_name[256];
+ size_t tmp_name_len;
+ char *dir;
+ size_t len;
+
+#ifdef P_tmpdir
+ len = (P_tmpdir != NULL) ? strlen(P_tmpdir) : 0;
+
+ if (len > 0 && len < sizeof(tmp_name)) {
+ strcpy(tmp_name, P_tmpdir);
+ tmp_name_len = len;
+ }
+ else {
+ strcpy(tmp_name, "/tmp");
+ tmp_name_len = 4;
+ }
+#else
+ strcpy(tmp_name, "/tmp");
+ tmp_name_len = 4;
+#endif
+
+ dir = secure_getenv("TMPDIR");
+ if (dir) {
+ len = strlen(dir);
+ if (len > 0 && len < sizeof(tmp_name)) {
+ strcpy(tmp_name, dir);
+ tmp_name_len = len;
+ }
+ }
+
+ SLJIT_ASSERT(tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name));
+
+ while (tmp_name_len > 0 && tmp_name[tmp_name_len - 1] == '/') {
+ tmp_name_len--;
+ tmp_name[tmp_name_len] = '\0';
+ }
+
+#ifdef O_TMPFILE
+ fd = open(tmp_name, O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ if (fd != -1)
+ return fd;
+#endif
+
+ if (tmp_name_len + 7 >= sizeof(tmp_name))
+ {
+ return -1;
+ }
+
+ strcpy(tmp_name + tmp_name_len, "/XXXXXX");
+ fd = mkostemp(tmp_name, O_CLOEXEC | O_NOATIME);
+
+ if (fd == -1)
+ return fd;
+
+ if (unlink(tmp_name)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size)
+{
+ struct chunk_header *retval;
+ int fd;
+
+ fd = create_tempfile();
+ if (fd == -1)
+ return NULL;
+
+ if (ftruncate(fd, size)) {
+ close(fd);
+ return NULL;
+ }
+
+ retval = (struct chunk_header *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (retval == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ retval->executable = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
+
+ if (retval->executable == MAP_FAILED) {
+ munmap(retval, size);
+ close(fd);
+ return NULL;
+ }
+
+ retval->fd = fd;
+ return retval;
+}
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ struct chunk_header *header = ((struct chunk_header *)chunk) - 1;
+
+ int fd = header->fd;
+ munmap(header->executable, size);
+ munmap(header, size);
+ close(fd);
+}
+
+/* --------------------------------------------------------------------- */
+/* Common functions */
+/* --------------------------------------------------------------------- */
+
+#define CHUNK_MASK (~(CHUNK_SIZE - 1))
+
+struct block_header {
+ sljit_uw size;
+ sljit_uw prev_size;
+ sljit_sw executable_offset;
+};
+
+struct free_block {
+ struct block_header header;
+ struct free_block *next;
+ struct free_block *prev;
+ sljit_uw size;
+};
+
+#define AS_BLOCK_HEADER(base, offset) \
+ ((struct block_header*)(((sljit_u8*)base) + offset))
+#define AS_FREE_BLOCK(base, offset) \
+ ((struct free_block*)(((sljit_u8*)base) + offset))
+#define MEM_START(base) ((void*)((base) + 1))
+#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7) & ~7)
+
+static struct free_block* free_blocks;
+static sljit_uw allocated_size;
+static sljit_uw total_size;
+
+static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
+{
+ free_block->header.size = 0;
+ free_block->size = size;
+
+ free_block->next = free_blocks;
+ free_block->prev = NULL;
+ if (free_blocks)
+ free_blocks->prev = free_block;
+ free_blocks = free_block;
+}
+
+static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
+{
+ if (free_block->next)
+ free_block->next->prev = free_block->prev;
+
+ if (free_block->prev)
+ free_block->prev->next = free_block->next;
+ else {
+ SLJIT_ASSERT(free_blocks == free_block);
+ free_blocks = free_block->next;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ struct chunk_header *chunk_header;
+ struct block_header *header;
+ struct block_header *next_header;
+ struct free_block *free_block;
+ sljit_uw chunk_size;
+ sljit_sw executable_offset;
+
+ allocator_grab_lock();
+ if (size < (64 - sizeof(struct block_header)))
+ size = (64 - sizeof(struct block_header));
+ size = ALIGN_SIZE(size);
+
+ free_block = free_blocks;
+ while (free_block) {
+ if (free_block->size >= size) {
+ chunk_size = free_block->size;
+ if (chunk_size > size + 64) {
+ /* We just cut a block from the end of the free block. */
+ chunk_size -= size;
+ free_block->size = chunk_size;
+ header = AS_BLOCK_HEADER(free_block, chunk_size);
+ header->prev_size = chunk_size;
+ header->executable_offset = free_block->header.executable_offset;
+ AS_BLOCK_HEADER(header, size)->prev_size = size;
+ }
+ else {
+ sljit_remove_free_block(free_block);
+ header = (struct block_header*)free_block;
+ size = chunk_size;
+ }
+ allocated_size += size;
+ header->size = size;
+ allocator_release_lock();
+ return MEM_START(header);
+ }
+ free_block = free_block->next;
+ }
+
+ chunk_size = sizeof(struct chunk_header) + sizeof(struct block_header);
+ chunk_size = (chunk_size + size + CHUNK_SIZE - 1) & CHUNK_MASK;
+
+ chunk_header = alloc_chunk(chunk_size);
+ if (!chunk_header) {
+ allocator_release_lock();
+ return NULL;
+ }
+
+ executable_offset = (sljit_sw)((sljit_u8*)chunk_header->executable - (sljit_u8*)chunk_header);
+
+ chunk_size -= sizeof(struct chunk_header) + sizeof(struct block_header);
+ total_size += chunk_size;
+
+ header = (struct block_header *)(chunk_header + 1);
+
+ header->prev_size = 0;
+ header->executable_offset = executable_offset;
+ if (chunk_size > size + 64) {
+ /* Cut the allocated space into a free and a used block. */
+ allocated_size += size;
+ header->size = size;
+ chunk_size -= size;
+
+ free_block = AS_FREE_BLOCK(header, size);
+ free_block->header.prev_size = size;
+ free_block->header.executable_offset = executable_offset;
+ sljit_insert_free_block(free_block, chunk_size);
+ next_header = AS_BLOCK_HEADER(free_block, chunk_size);
+ }
+ else {
+ /* All space belongs to this allocation. */
+ allocated_size += chunk_size;
+ header->size = chunk_size;
+ next_header = AS_BLOCK_HEADER(header, chunk_size);
+ }
+ next_header->size = 1;
+ next_header->prev_size = chunk_size;
+ next_header->executable_offset = executable_offset;
+ allocator_release_lock();
+ return MEM_START(header);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ struct block_header *header;
+ struct free_block* free_block;
+
+ allocator_grab_lock();
+ header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
+ header = AS_BLOCK_HEADER(header, -header->executable_offset);
+ allocated_size -= header->size;
+
+ /* Connecting free blocks together if possible. */
+
+ /* If header->prev_size == 0, free_block will equal to header.
+ In this case, free_block->header.size will be > 0. */
+ free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
+ if (SLJIT_UNLIKELY(!free_block->header.size)) {
+ free_block->size += header->size;
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+ else {
+ free_block = (struct free_block*)header;
+ sljit_insert_free_block(free_block, header->size);
+ }
+
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ if (SLJIT_UNLIKELY(!header->size)) {
+ free_block->size += ((struct free_block*)header)->size;
+ sljit_remove_free_block((struct free_block*)header);
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+
+ /* The whole chunk is free. */
+ if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
+ /* If this block is freed, we still have (allocated_size / 2) free space. */
+ if (total_size - free_block->size > (allocated_size * 3 / 2)) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ }
+
+ allocator_release_lock();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ struct free_block* free_block;
+ struct free_block* next_free_block;
+
+ allocator_grab_lock();
+
+ free_block = free_blocks;
+ while (free_block) {
+ next_free_block = free_block->next;
+ if (!free_block->header.prev_size &&
+ AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ free_block = next_free_block;
+ }
+
+ SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
+ allocator_release_lock();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr)
+{
+ return ((struct block_header *)(ptr))[-1].executable_offset;
+}
diff --git a/thirdparty/pcre2/src/sljit/sljitUtils.c b/thirdparty/pcre2/src/sljit/sljitUtils.c
index ec5c321194..5c2a838932 100644
--- a/thirdparty/pcre2/src/sljit/sljitUtils.c
+++ b/thirdparty/pcre2/src/sljit/sljitUtils.c
@@ -1,7 +1,7 @@
/*
* Stack-less Just-In-Time compiler
*
- * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -48,12 +48,12 @@ static SLJIT_INLINE void allocator_release_lock(void)
#if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK)
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void)
{
/* Always successful. */
}
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void)
{
/* Always successful. */
}
@@ -88,7 +88,7 @@ static SLJIT_INLINE void allocator_release_lock(void)
static HANDLE global_mutex = 0;
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void)
{
/* No idea what to do if an error occures. Static mutexes should never fail... */
if (!global_mutex)
@@ -97,7 +97,7 @@ SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void)
WaitForSingleObject(global_mutex, INFINITE);
}
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void)
{
ReleaseMutex(global_mutex);
}
@@ -130,12 +130,12 @@ static SLJIT_INLINE void allocator_release_lock(void)
static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void)
{
pthread_mutex_lock(&global_mutex);
}
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void)
{
pthread_mutex_unlock(&global_mutex);
}
@@ -203,19 +203,16 @@ static SLJIT_INLINE sljit_s32 open_dev_zero(void)
/* Planning to make it even more clever in the future. */
static sljit_sw sljit_page_align = 0;
-SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit, void *allocator_data)
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
{
struct sljit_stack *stack;
- union {
- void *ptr;
- sljit_uw uw;
- } base;
+ void *ptr;
#ifdef _WIN32
SYSTEM_INFO si;
#endif
SLJIT_UNUSED_ARG(allocator_data);
- if (limit > max_limit || limit < 1)
+ if (start_size > max_size || start_size < 1)
return NULL;
#ifdef _WIN32
@@ -233,29 +230,31 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(slj
}
#endif
- /* Align limit and max_limit. */
- max_limit = (max_limit + sljit_page_align) & ~sljit_page_align;
-
stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
if (!stack)
return NULL;
+ /* Align max_size. */
+ max_size = (max_size + sljit_page_align) & ~sljit_page_align;
+
#ifdef _WIN32
- base.ptr = VirtualAlloc(NULL, max_limit, MEM_RESERVE, PAGE_READWRITE);
- if (!base.ptr) {
+ ptr = VirtualAlloc(NULL, max_size, MEM_RESERVE, PAGE_READWRITE);
+ if (!ptr) {
SLJIT_FREE(stack, allocator_data);
return NULL;
}
- stack->base = base.uw;
- stack->limit = stack->base;
- stack->max_limit = stack->base + max_limit;
- if (sljit_stack_resize(stack, stack->base + limit)) {
+
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end;
+
+ if (sljit_stack_resize(stack, stack->end - start_size) == NULL) {
sljit_free_stack(stack, allocator_data);
return NULL;
}
#else
#ifdef MAP_ANON
- base.ptr = mmap(NULL, max_limit, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
#else
if (dev_zero < 0) {
if (open_dev_zero()) {
@@ -263,73 +262,70 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(slj
return NULL;
}
}
- base.ptr = mmap(NULL, max_limit, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0);
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0);
#endif
- if (base.ptr == MAP_FAILED) {
+ if (ptr == MAP_FAILED) {
SLJIT_FREE(stack, allocator_data);
return NULL;
}
- stack->base = base.uw;
- stack->limit = stack->base + limit;
- stack->max_limit = stack->base + max_limit;
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end - start_size;
#endif
- stack->top = stack->base;
+ stack->top = stack->end;
return stack;
}
#undef PAGE_ALIGN
-SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack* stack, void *allocator_data)
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
{
SLJIT_UNUSED_ARG(allocator_data);
#ifdef _WIN32
- VirtualFree((void*)stack->base, 0, MEM_RELEASE);
+ VirtualFree((void*)stack->min_start, 0, MEM_RELEASE);
#else
- munmap((void*)stack->base, stack->max_limit - stack->base);
+ munmap((void*)stack->min_start, stack->end - stack->min_start);
#endif
SLJIT_FREE(stack, allocator_data);
}
-SLJIT_API_FUNC_ATTRIBUTE sljit_sw SLJIT_CALL sljit_stack_resize(struct sljit_stack* stack, sljit_uw new_limit)
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
{
- sljit_uw aligned_old_limit;
- sljit_uw aligned_new_limit;
+ sljit_uw aligned_old_start;
+ sljit_uw aligned_new_start;
+
+ if ((new_start < stack->min_start) || (new_start >= stack->end))
+ return NULL;
- if ((new_limit > stack->max_limit) || (new_limit < stack->base))
- return -1;
#ifdef _WIN32
- aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align;
- aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align;
- if (aligned_new_limit != aligned_old_limit) {
- if (aligned_new_limit > aligned_old_limit) {
- if (!VirtualAlloc((void*)aligned_old_limit, aligned_new_limit - aligned_old_limit, MEM_COMMIT, PAGE_READWRITE))
- return -1;
+ aligned_new_start = (sljit_uw)new_start & ~sljit_page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align;
+ if (aligned_new_start != aligned_old_start) {
+ if (aligned_new_start < aligned_old_start) {
+ if (!VirtualAlloc((void*)aligned_new_start, aligned_old_start - aligned_new_start, MEM_COMMIT, PAGE_READWRITE))
+ return NULL;
}
else {
- if (!VirtualFree((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MEM_DECOMMIT))
- return -1;
+ if (!VirtualFree((void*)aligned_old_start, aligned_new_start - aligned_old_start, MEM_DECOMMIT))
+ return NULL;
}
}
- stack->limit = new_limit;
- return 0;
#else
- if (new_limit >= stack->limit) {
- stack->limit = new_limit;
- return 0;
- }
- aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align;
- aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align;
- /* If madvise is available, we release the unnecessary space. */
+ if (stack->start < new_start) {
+ aligned_new_start = (sljit_uw)new_start & ~sljit_page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align;
+ /* If madvise is available, we release the unnecessary space. */
#if defined(MADV_DONTNEED)
- if (aligned_new_limit < aligned_old_limit)
- madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MADV_DONTNEED);
+ if (aligned_new_start > aligned_old_start)
+ madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_DONTNEED);
#elif defined(POSIX_MADV_DONTNEED)
- if (aligned_new_limit < aligned_old_limit)
- posix_madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, POSIX_MADV_DONTNEED);
+ if (aligned_new_start > aligned_old_start)
+ posix_madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, POSIX_MADV_DONTNEED);
#endif
- stack->limit = new_limit;
- return 0;
+ }
#endif
+ stack->start = new_start;
+ return new_start;
}
#endif /* SLJIT_UTIL_STACK */